git.delta.rocks / unique-network / refs/commits / 4dff934f02cf

difftreelog

Merge branch 'develop' into feature/sapphire-is-quartz

Yaroslav Bolyukin2022-12-29parents: #13ad712 #a2c419c.patch.diff
in: master

55 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4026,7 +4026,7 @@
  "pallet-elections-phragmen",
  "pallet-fast-unstake",
  "pallet-grandpa",
- "pallet-identity",
+ "pallet-identity 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.36)",
  "pallet-im-online",
  "pallet-indices",
  "pallet-membership",
@@ -5307,8 +5307,10 @@
  "orml-xtokens",
  "pallet-app-promotion",
  "pallet-aura",
+ "pallet-authorship",
  "pallet-balances",
  "pallet-base-fee",
+ "pallet-collator-selection",
  "pallet-common",
  "pallet-configuration",
  "pallet-ethereum",
@@ -5320,6 +5322,7 @@
  "pallet-evm-transaction-payment",
  "pallet-foreign-assets",
  "pallet-fungible",
+ "pallet-identity 4.0.0-dev",
  "pallet-inflation",
  "pallet-maintenance",
  "pallet-nonfungible",
@@ -5327,6 +5330,7 @@
  "pallet-refungible",
  "pallet-rmrk-core",
  "pallet-rmrk-equip",
+ "pallet-session",
  "pallet-structure",
  "pallet-sudo",
  "pallet-template-transaction-payment",
@@ -5772,6 +5776,33 @@
 ]
 
 [[package]]
+name = "pallet-collator-selection"
+version = "4.0.0"
+dependencies = [
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
+ "log",
+ "pallet-aura",
+ "pallet-authorship",
+ "pallet-balances",
+ "pallet-configuration",
+ "pallet-session",
+ "pallet-timestamp",
+ "parity-scale-codec 3.2.1",
+ "rand 0.8.5",
+ "scale-info",
+ "serde",
+ "sp-consensus-aura",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-staking",
+ "sp-std",
+ "sp-tracing",
+]
+
+[[package]]
 name = "pallet-collective"
 version = "4.0.0-dev"
 source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.36#cb4f2491b00af7d7817f3a54209c26b20faa1f51"
@@ -5812,9 +5843,10 @@
 
 [[package]]
 name = "pallet-configuration"
-version = "0.1.2"
+version = "0.1.3"
 dependencies = [
  "fp-evm",
+ "frame-benchmarking",
  "frame-support",
  "frame-system",
  "parity-scale-codec 3.2.1",
@@ -6153,6 +6185,23 @@
 [[package]]
 name = "pallet-identity"
 version = "4.0.0-dev"
+dependencies = [
+ "enumflags2",
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
+ "pallet-balances",
+ "parity-scale-codec 3.2.1",
+ "scale-info",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+]
+
+[[package]]
+name = "pallet-identity"
+version = "4.0.0-dev"
 source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.36#cb4f2491b00af7d7817f3a54209c26b20faa1f51"
 dependencies = [
  "enumflags2",
@@ -8106,7 +8155,7 @@
  "pallet-elections-phragmen",
  "pallet-fast-unstake",
  "pallet-grandpa",
- "pallet-identity",
+ "pallet-identity 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.36)",
  "pallet-im-online",
  "pallet-indices",
  "pallet-membership",
@@ -8866,8 +8915,10 @@
  "orml-xtokens",
  "pallet-app-promotion",
  "pallet-aura",
+ "pallet-authorship",
  "pallet-balances",
  "pallet-base-fee",
+ "pallet-collator-selection",
  "pallet-common",
  "pallet-configuration",
  "pallet-ethereum",
@@ -8879,6 +8930,7 @@
  "pallet-evm-transaction-payment",
  "pallet-foreign-assets",
  "pallet-fungible",
+ "pallet-identity 4.0.0-dev",
  "pallet-inflation",
  "pallet-maintenance",
  "pallet-nonfungible",
@@ -8886,6 +8938,7 @@
  "pallet-refungible",
  "pallet-rmrk-core",
  "pallet-rmrk-equip",
+ "pallet-session",
  "pallet-structure",
  "pallet-sudo",
  "pallet-template-transaction-payment",
@@ -9318,7 +9371,7 @@
  "pallet-democracy",
  "pallet-elections-phragmen",
  "pallet-grandpa",
- "pallet-identity",
+ "pallet-identity 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.36)",
  "pallet-im-online",
  "pallet-indices",
  "pallet-membership",
@@ -13094,8 +13147,10 @@
  "orml-xtokens",
  "pallet-app-promotion",
  "pallet-aura",
+ "pallet-authorship",
  "pallet-balances",
  "pallet-base-fee",
+ "pallet-collator-selection",
  "pallet-common",
  "pallet-configuration",
  "pallet-ethereum",
@@ -13107,6 +13162,7 @@
  "pallet-evm-transaction-payment",
  "pallet-foreign-assets",
  "pallet-fungible",
+ "pallet-identity 4.0.0-dev",
  "pallet-inflation",
  "pallet-maintenance",
  "pallet-nonfungible",
@@ -13114,6 +13170,7 @@
  "pallet-refungible",
  "pallet-rmrk-core",
  "pallet-rmrk-equip",
+ "pallet-session",
  "pallet-structure",
  "pallet-sudo",
  "pallet-template-transaction-payment",
@@ -13750,7 +13807,7 @@
  "pallet-elections-phragmen",
  "pallet-fast-unstake",
  "pallet-grandpa",
- "pallet-identity",
+ "pallet-identity 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.36)",
  "pallet-im-online",
  "pallet-indices",
  "pallet-membership",
modifiedMakefilediffbeforeafterboth
--- a/Makefile
+++ b/Makefile
@@ -89,6 +89,10 @@
 bench-evm-migration:
 	make _bench PALLET=evm-migration
 
+.PHONY: bench-configuration
+bench-configuration:
+	make _bench PALLET=configuration
+
 .PHONY: bench-common
 bench-common:
 	make _bench PALLET=common
@@ -127,12 +131,20 @@
 
 .PHONY: bench-foreign-assets
 bench-foreign-assets:
-	make _bench PALLET=foreign-assets	
+	make _bench PALLET=foreign-assets
+
+.PHONY: bench-collator-selection
+bench-collator-selection:
+	make _bench PALLET=collator-selection
+
+.PHONY: bench-identity
+bench-identity:
+	make _bench PALLET=identity
 
 .PHONY: bench-app-promotion
 bench-app-promotion:
 	make _bench PALLET=app-promotion PALLET_DIR=app-promotion
 	
 .PHONY: bench
-# Disabled: bench-scheduler, bench-rmrk-core, bench-rmrk-equip
-bench: bench-evm-migration bench-unique bench-structure bench-fungible bench-refungible bench-nonfungible bench-foreign-assets
+# Disabled: bench-scheduler, bench-collator-selection, bench-rmrk-core, bench-rmrk-equip
+bench: bench-evm-migration bench-unique bench-structure bench-fungible bench-refungible bench-nonfungible bench-configuration bench-foreign-assets bench-identity
modifiedlaunch-config.jsondiffbeforeafterboth
--- a/launch-config.json
+++ b/launch-config.json
@@ -85,7 +85,7 @@
     "parachains": [
         {
             "bin": "../unique-chain/target/release/unique-collator",
-            "id": "1000",
+            "id": 1000,
             "balance": "1000000000000000000000000",
             "nodes": [
                 {
@@ -111,6 +111,42 @@
                         "--unsafe-ws-external",
                         "-lxcm=trace"
                     ]
+                },
+                {
+                    "port": 31202,
+                    "wsPort": 9946,
+                    "rpcPort": 9935,
+                    "name": "charlie",
+                    "flags": [
+                        "--rpc-cors=all",
+                        "--unsafe-rpc-external",
+                        "--unsafe-ws-external",
+                        "-lxcm=trace"
+                    ]
+                },
+                {
+                    "port": 31203,
+                    "wsPort": 9947,
+                    "rpcPort": 9936,
+                    "name": "dave",
+                    "flags": [
+                        "--rpc-cors=all",
+                        "--unsafe-rpc-external",
+                        "--unsafe-ws-external",
+                        "-lxcm=trace"
+                    ]
+                },
+                {
+                    "port": 31204,
+                    "wsPort": 9948,
+                    "rpcPort": 9937,
+                    "name": "eve",
+                    "flags": [
+                        "--rpc-cors=all",
+                        "--unsafe-rpc-external",
+                        "--unsafe-ws-external",
+                        "-lxcm=trace"
+                    ]
                 }
             ]
         }
modifiednode/cli/src/chain_spec.rsdiffbeforeafterboth
--- a/node/cli/src/chain_spec.rs
+++ b/node/cli/src/chain_spec.rs
@@ -143,11 +143,76 @@
 	AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()
 }
 
+#[cfg(feature = "quartz-runtime")]
+macro_rules! testnet_genesis {
+	(
+		$runtime:path,
+		$root_key:expr,
+		$initial_invulnerables:expr,
+		$endowed_accounts:expr,
+		$id:expr
+	) => {{
+		use $runtime::*;
+
+		GenesisConfig {
+			system: SystemConfig {
+				code: WASM_BINARY
+					.expect("WASM binary was not build, please build it!")
+					.to_vec(),
+			},
+			balances: BalancesConfig {
+				balances: $endowed_accounts
+					.iter()
+					.cloned()
+					// 1e13 UNQ
+					.map(|k| (k, 1 << 100))
+					.collect(),
+			},
+			treasury: Default::default(),
+			tokens: TokensConfig { balances: vec![] },
+			sudo: SudoConfig {
+				key: Some($root_key),
+			},
+			vesting: VestingConfig { vesting: vec![] },
+			parachain_info: ParachainInfoConfig {
+				parachain_id: $id.into(),
+			},
+			parachain_system: Default::default(),
+			collator_selection: CollatorSelectionConfig {
+				invulnerables: $initial_invulnerables
+					.iter()
+					.cloned()
+					.map(|(acc, _)| acc)
+					.collect(),
+			},
+			session: SessionConfig {
+				keys: $initial_invulnerables
+					.into_iter()
+					.map(|(acc, aura)| {
+						(
+							acc.clone(),          // account id
+							acc,                  // validator id
+							SessionKeys { aura }, // session keys
+						)
+					})
+					.collect(),
+			},
+			aura: Default::default(),
+			aura_ext: Default::default(),
+			evm: EVMConfig {
+				accounts: BTreeMap::new(),
+			},
+			ethereum: EthereumConfig {},
+		}
+	}};
+}
+
+#[cfg(not(feature = "quartz-runtime"))]
 macro_rules! testnet_genesis {
 	(
 		$runtime:path,
 		$root_key:expr,
-		$initial_authorities:expr,
+		$initial_invulnerables:expr,
 		$endowed_accounts:expr,
 		$id:expr
 	) => {{
@@ -178,7 +243,10 @@
 			},
 			parachain_system: Default::default(),
 			aura: AuraConfig {
-				authorities: $initial_authorities,
+				authorities: $initial_invulnerables
+					.into_iter()
+					.map(|(_, aura)| aura)
+					.collect(),
 			},
 			aura_ext: Default::default(),
 			evm: EVMConfig {
@@ -219,8 +287,14 @@
 				// Sudo account
 				get_account_id_from_seed::<sr25519::Public>("Alice"),
 				vec![
-					get_from_seed::<AuraId>("Alice"),
-					get_from_seed::<AuraId>("Bob"),
+					(
+						get_account_id_from_seed::<sr25519::Public>("Alice"),
+						get_from_seed::<AuraId>("Alice"),
+					),
+					(
+						get_account_id_from_seed::<sr25519::Public>("Bob"),
+						get_from_seed::<AuraId>("Bob"),
+					),
 				],
 				// Pre-funded accounts
 				vec![
@@ -287,8 +361,14 @@
 				// Sudo account
 				get_account_id_from_seed::<sr25519::Public>("Alice"),
 				vec![
-					get_from_seed::<AuraId>("Alice"),
-					get_from_seed::<AuraId>("Bob"),
+					(
+						get_account_id_from_seed::<sr25519::Public>("Alice"),
+						get_from_seed::<AuraId>("Alice"),
+					),
+					(
+						get_account_id_from_seed::<sr25519::Public>("Bob"),
+						get_from_seed::<AuraId>("Bob"),
+					),
 				],
 				// Pre-funded accounts
 				vec![
modifiednode/cli/src/service.rsdiffbeforeafterboth
--- a/node/cli/src/service.rs
+++ b/node/cli/src/service.rs
@@ -973,7 +973,7 @@
 				.import_notification_stream()
 				.map(|_| EngineCommand::SealNewBlock {
 					create_empty: true,
-					finalize: false,
+					finalize: false, // todo:collator finalize true
 					parent_hash: None,
 					sender: None,
 				}),
@@ -984,7 +984,7 @@
 			dyn Stream<Item = EngineCommand<Hash>> + Send + Sync + Unpin,
 		> = Box::new(autoseal_interval.map(|_| EngineCommand::SealNewBlock {
 			create_empty: true,
-			finalize: false,
+			finalize: false, // todo:collator finalize true
 			parent_hash: None,
 			sender: None,
 		}));
addedpallets/collator-selection/CHANGELOG.mddiffbeforeafterboth
--- /dev/null
+++ b/pallets/collator-selection/CHANGELOG.md
@@ -0,0 +1,25 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+
+<!-- bureaucracy goes here -->
+
+## [4.0.0] - 2022-12-29
+
+### Added
+
+- Entire functionality moved over from cumulus/pallet-collator-selection (v3.0.0). Refactored business logic:
+   - Added an extra step for candidacy, `get_license`, at which payment happens. The number of license holders is unlimited. 
+      - Only licensed accounts can apply for candidacy with `onboard`. No extra deposits are made.
+      - Active candidates may `offboard`, but they will retain their license.
+      - Deposit is returned, and candidacy possibly removed with `release_license`.
+      - License can be forcibly forfeited and candidacy removed with `force_release_license`.
+   - Failing collators' deposits will now be fully slashed, and funds will be redirected to Treasury.
+   - Unify `MaxInvulnerables` and `MaxCandidates` into `MaxCollators`.
+   - Remove `MinCandidates`.
+   - Minimal amount of invulnerables is now 1.
+   - Both invulnerables and candidates count against the limits together, however invulnerables ignore `DesiredCollators`.
+   - `KickThreshold` is made configurable.
+   - `DesiredCollators`, `LicenseBond`, and `KickThreshold` are moved to and referenced from `pallet-configuration`.
+   - Naming changes to better reflect the new functionality.
+   - More minor changes, tests, benchmarks, etc.
\ No newline at end of file
addedpallets/collator-selection/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/pallets/collator-selection/Cargo.toml
@@ -0,0 +1,64 @@
+[package]
+name = "pallet-collator-selection"
+version = "4.0.0"
+description = "Simple staking pallet with a fixed stake."
+authors = ["Parity Technologies <admin@parity.io>", "Unique Network <support@uniquenetwork.io>"]
+edition = "2021"
+homepage = "https://unique.network"
+repository = "https://github.com/UniqueNetwork/unique-chain"
+license = "GPLv3"
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
+
+[dependencies]
+log = { version = "0.4.17", default-features = false }
+codec = { default-features = false, features = ["derive"], package = "parity-scale-codec", version = "3.0.0" }
+rand = { version = "0.8.5", features = ["std_rng"], default-features = false }
+scale-info = { version = "2.2.0", default-features = false, features = ["derive"] }
+serde = { version = "1.0.145", default-features = false }
+
+sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+sp-staking = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+frame-support = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+frame-system = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+pallet-authorship = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+pallet-session = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+pallet-configuration = { default-features = false, path = "../configuration" }
+
+frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+
+[dev-dependencies]
+sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+sp-tracing = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+pallet-aura = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+
+[features]
+default = ["std"]
+runtime-benchmarks = [
+	"frame-benchmarking/runtime-benchmarks",
+	"frame-support/runtime-benchmarks",
+	"frame-system/runtime-benchmarks",
+]
+std = [
+	"codec/std",
+	"log/std",
+	"scale-info/std",
+	"rand/std",
+	"sp-runtime/std",
+	"sp-staking/std",
+	"sp-std/std",
+	"frame-support/std",
+	"frame-system/std",
+	"frame-benchmarking/std",
+	"pallet-authorship/std",
+	"pallet-session/std",
+]
+
+try-runtime = [ "frame-support/try-runtime" ]
addedpallets/collator-selection/src/benchmarking.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/collator-selection/src/benchmarking.rs
@@ -0,0 +1,388 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+// Original license:
+// Copyright (C) 2021 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.
+
+//! Benchmarking setup for pallet-collator-selection
+
+use super::*;
+
+#[allow(unused)]
+use crate::Pallet as CollatorSelection;
+use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite, whitelisted_caller};
+use frame_support::{
+	assert_ok,
+	codec::Decode,
+	traits::{Currency, EnsureOrigin, Get},
+};
+use frame_system::{EventRecord, RawOrigin};
+use pallet_authorship::EventHandler;
+use pallet_session::{self as session, SessionManager};
+use pallet_configuration::{
+	self as configuration, BalanceOf,
+	CollatorSelectionDesiredCollatorsOverride as DesiredCollators,
+	CollatorSelectionLicenseBondOverride as LicenseBond,
+};
+use sp_std::prelude::*;
+
+const SEED: u32 = 0;
+
+// TODO: remove if this is given in substrate commit.
+macro_rules! whitelist {
+	($acc:ident) => {
+		frame_benchmarking::benchmarking::add_to_whitelist(
+			frame_system::Account::<T>::hashed_key_for(&$acc).into(),
+		);
+	};
+}
+
+fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
+	let events = frame_system::Pallet::<T>::events();
+	let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into();
+	// compare to the last event record
+	let EventRecord { event, .. } = &events[events.len() - 1];
+	assert_eq!(event, &system_event);
+}
+
+fn create_funded_user<T: Config>(
+	string: &'static str,
+	n: u32,
+	balance_factor: u32,
+) -> T::AccountId {
+	let user = account(string, n, SEED);
+	let balance = T::Currency::minimum_balance() * balance_factor.into();
+	let _ = T::Currency::make_free_balance_be(&user, balance);
+	user
+}
+
+fn keys<T: Config + session::Config>(c: u32) -> <T as session::Config>::Keys {
+	use rand::{RngCore, SeedableRng};
+
+	let keys = {
+		let mut keys = [0u8; 128];
+
+		if c > 0 {
+			let mut rng = rand::rngs::StdRng::seed_from_u64(c as u64);
+			rng.fill_bytes(&mut keys);
+		}
+
+		keys
+	};
+
+	Decode::decode(&mut &keys[..]).unwrap()
+}
+
+fn validator<T: Config + session::Config>(c: u32) -> (T::AccountId, <T as session::Config>::Keys) {
+	(create_funded_user::<T>("candidate", c, 1000), keys::<T>(c))
+}
+
+fn register_validators<T: Config + session::Config>(count: u32) -> Vec<T::AccountId> {
+	let validators = (0..count).map(|c| validator::<T>(c)).collect::<Vec<_>>();
+
+	for (who, keys) in validators.clone() {
+		<session::Pallet<T>>::set_keys(RawOrigin::Signed(who).into(), keys, Vec::new()).unwrap();
+	}
+
+	validators.into_iter().map(|(who, _)| who).collect()
+}
+
+fn register_invulnerables<T: Config + configuration::Config>(count: u32) {
+	let candidates = (0..count)
+		.map(|c| account("candidate", c, SEED))
+		.collect::<Vec<_>>();
+
+	for who in candidates {
+		<CollatorSelection<T>>::add_invulnerable(T::UpdateOrigin::successful_origin(), who)
+			.unwrap();
+	}
+}
+
+fn register_candidates<T: Config + configuration::Config>(count: u32) {
+	let candidates = (0..count)
+		.map(|c| account("candidate", c, SEED))
+		.collect::<Vec<_>>();
+	assert!(
+		<LicenseBond<T>>::get() > 0u32.into(),
+		"Bond cannot be zero!"
+	);
+
+	for who in candidates {
+		T::Currency::make_free_balance_be(&who, <LicenseBond<T>>::get() * 2u32.into());
+		<CollatorSelection<T>>::get_license(RawOrigin::Signed(who.clone()).into()).unwrap();
+		<CollatorSelection<T>>::onboard(RawOrigin::Signed(who).into()).unwrap();
+	}
+}
+
+fn get_licenses<T: Config + configuration::Config>(count: u32) {
+	let candidates = (0..count)
+		.map(|c| account("candidate", c, SEED))
+		.collect::<Vec<_>>();
+	assert!(
+		<LicenseBond<T>>::get() > 0u32.into(),
+		"Bond cannot be zero!"
+	);
+
+	for who in candidates {
+		T::Currency::make_free_balance_be(&who, <LicenseBond<T>>::get() * 2u32.into());
+		<CollatorSelection<T>>::get_license(RawOrigin::Signed(who.clone()).into()).unwrap();
+	}
+}
+
+benchmarks! {
+	where_clause { where T: pallet_authorship::Config + session::Config + configuration::Config }
+
+	// todo:collator this and all the following do not work for some reason, going all the way up to 10 in length
+	// Both invulnerables and candidates count together against MaxCollators.
+	// Maybe try putting it in braces? 1 .. (T::MaxCollators::get() - 2)
+	add_invulnerable {
+		let b in 1 .. T::MaxCollators::get() - 3;
+		register_validators::<T>(b);
+		register_invulnerables::<T>(b);
+
+		// log::info!("{} {}", <Invulnerables<T>>::get().len(), b);
+
+		let new_invulnerable: T::AccountId = whitelisted_caller();
+		let bond: BalanceOf<T> = T::Currency::minimum_balance() * 2u32.into();
+		T::Currency::make_free_balance_be(&new_invulnerable, bond.clone());
+
+		<session::Pallet<T>>::set_keys(
+			RawOrigin::Signed(new_invulnerable.clone()).into(),
+			keys::<T>(b + 1),
+			Vec::new()
+		).unwrap();
+
+		let root_origin = T::UpdateOrigin::successful_origin();
+	}: {
+		assert_ok!(
+			<CollatorSelection<T>>::add_invulnerable(root_origin, new_invulnerable.clone())
+		);
+	}
+	verify {
+		assert_last_event::<T>(Event::InvulnerableAdded{invulnerable: new_invulnerable}.into());
+	}
+
+	remove_invulnerable {
+		let b in 1 .. T::MaxCollators::get();
+		register_validators::<T>(b);
+		register_invulnerables::<T>(b);
+
+		let root_origin = T::UpdateOrigin::successful_origin();
+		let leaving = <Invulnerables<T>>::get().last().unwrap().clone();
+		whitelist!(leaving);
+	}: {
+		assert_ok!(
+			<CollatorSelection<T>>::remove_invulnerable(root_origin, leaving.clone())
+		);
+	}
+	verify {
+		assert_last_event::<T>(Event::InvulnerableRemoved{invulnerable: leaving}.into());
+	}
+
+	get_license {
+		let c in 1 .. T::MaxCollators::get();
+
+		<LicenseBond<T>>::put(T::Currency::minimum_balance());
+
+		register_validators::<T>(c);
+		get_licenses::<T>(c);
+
+		let caller: T::AccountId = whitelisted_caller();
+		let bond: BalanceOf<T> = T::Currency::minimum_balance() * 2u32.into();
+		T::Currency::make_free_balance_be(&caller, bond.clone());
+
+		<session::Pallet<T>>::set_keys(
+			RawOrigin::Signed(caller.clone()).into(),
+			keys::<T>(c + 1),
+			Vec::new()
+		).unwrap();
+
+	}: _(RawOrigin::Signed(caller.clone()))
+	verify {
+		assert_last_event::<T>(Event::LicenseObtained{account_id: caller, deposit: bond / 2u32.into()}.into());
+	}
+
+	// worst case is when we have all the max-candidate slots filled except one, and we fill that
+	// one.
+	onboard {
+		let c in 1 .. 5;
+
+		<LicenseBond<T>>::put(T::Currency::minimum_balance());
+		<DesiredCollators<T>>::put(c + 2);
+
+		register_validators::<T>(c);
+		register_candidates::<T>(c);
+
+		let caller: T::AccountId = whitelisted_caller();
+		let bond: BalanceOf<T> = T::Currency::minimum_balance() * 2u32.into();
+		T::Currency::make_free_balance_be(&caller, bond.clone());
+
+		let origin = RawOrigin::Signed(caller.clone());
+
+		<session::Pallet<T>>::set_keys(
+			origin.clone().into(),
+			keys::<T>(c + 1),
+			Vec::new()
+		).unwrap();
+
+		assert_ok!(
+			<CollatorSelection<T>>::get_license(origin.clone().into())
+		);
+	}: _(origin)
+	verify {
+		assert_last_event::<T>(Event::CandidateAdded{account_id: caller}.into());
+	}
+
+	// worst case is the last candidate leaving.
+	offboard {
+		let c in 1 .. T::MaxCollators::get();
+		<LicenseBond<T>>::put(T::Currency::minimum_balance());
+		<DesiredCollators<T>>::put(c + 2);
+
+		register_validators::<T>(c);
+		register_candidates::<T>(c);
+
+		let leaving = <Candidates<T>>::get().last().unwrap().clone();
+		whitelist!(leaving);
+	}: _(RawOrigin::Signed(leaving.clone()))
+	verify {
+		assert_last_event::<T>(Event::CandidateRemoved{account_id: leaving}.into());
+	}
+
+	// worst case is the last candidate leaving.
+	release_license {
+		let c in 1 .. T::MaxCollators::get();
+		let bond = T::Currency::minimum_balance();
+		<LicenseBond<T>>::put(bond);
+		<DesiredCollators<T>>::put(c);
+
+		register_validators::<T>(c);
+		register_candidates::<T>(c);
+
+		let leaving = <Candidates<T>>::get().last().unwrap().clone();
+		whitelist!(leaving);
+	}: _(RawOrigin::Signed(leaving.clone()))
+	verify {
+		assert_last_event::<T>(Event::LicenseReleased{account_id: leaving, deposit_returned: bond}.into());
+	}
+
+	// worst case is the last candidate leaving.
+	force_release_license {
+		let c in 1 .. T::MaxCollators::get();
+		let bond = T::Currency::minimum_balance();
+		<LicenseBond<T>>::put(bond);
+		<DesiredCollators<T>>::put(c);
+
+		register_validators::<T>(c);
+		register_candidates::<T>(c);
+
+		let leaving = <Candidates<T>>::get().last().unwrap().clone();
+		whitelist!(leaving);
+		let origin = T::UpdateOrigin::successful_origin();
+	}: {
+		assert_ok!(
+			<CollatorSelection<T>>::force_release_license(origin, leaving.clone())
+		);
+	}
+	verify {
+		assert_last_event::<T>(Event::LicenseReleased{account_id: leaving, deposit_returned: bond}.into());
+	}
+
+	// worst case is paying a non-existing candidate account.
+	note_author {
+		<LicenseBond<T>>::put(T::Currency::minimum_balance());
+		T::Currency::make_free_balance_be(
+			&<CollatorSelection<T>>::account_id(),
+			T::Currency::minimum_balance() * 4u32.into(),
+		);
+		let author = account("author", 0, SEED);
+		let new_block: T::BlockNumber = 10u32.into();
+
+		frame_system::Pallet::<T>::set_block_number(new_block);
+		assert!(T::Currency::free_balance(&author) == 0u32.into());
+	}: {
+		<CollatorSelection<T> as EventHandler<_, _>>::note_author(author.clone())
+	} verify {
+		assert!(T::Currency::free_balance(&author) > 0u32.into());
+		assert_eq!(frame_system::Pallet::<T>::block_number(), new_block);
+	}
+
+	// worst case for new session.
+	new_session {
+		let r in 1 .. T::MaxCollators::get();
+		let c in 1 .. T::MaxCollators::get();
+
+		<LicenseBond<T>>::put(T::Currency::minimum_balance());
+		<DesiredCollators<T>>::put(c);
+		frame_system::Pallet::<T>::set_block_number(0u32.into());
+
+		register_validators::<T>(c);
+		register_candidates::<T>(c);
+
+		let new_block: T::BlockNumber = 1800u32.into();
+		let zero_block: T::BlockNumber = 0u32.into();
+		let candidates = <Candidates<T>>::get();
+
+		let non_removals = c.saturating_sub(r);
+
+		for i in 0..c {
+			<LastAuthoredBlock<T>>::insert(candidates[i as usize].clone(), zero_block);
+		}
+
+		if non_removals > 0 {
+			for i in 0..non_removals {
+				<LastAuthoredBlock<T>>::insert(candidates[i as usize].clone(), new_block);
+			}
+		} else {
+			for i in 0..c {
+				<LastAuthoredBlock<T>>::insert(candidates[i as usize].clone(), new_block);
+			}
+		}
+
+		let pre_length = <Candidates<T>>::get().len();
+
+		frame_system::Pallet::<T>::set_block_number(new_block);
+
+		assert!(<Candidates<T>>::get().len() == c as usize);
+	}: {
+		<CollatorSelection<T> as SessionManager<_>>::new_session(0)
+	} verify {
+		if c > r {
+			assert!(<Candidates<T>>::get().len() < pre_length);
+		} else {
+			assert!(<Candidates<T>>::get().len() == pre_length);
+		}
+	}
+}
+
+impl_benchmark_test_suite!(
+	CollatorSelection,
+	crate::mock::new_test_ext(),
+	crate::mock::Test,
+);
addedpallets/collator-selection/src/lib.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/collator-selection/src/lib.rs
@@ -0,0 +1,649 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+// Original license:
+// Copyright (C) 2021 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.
+
+// todo:collator documentation
+//! Collator Selection pallet.
+//!
+//! A pallet to manage collators in a parachain.
+//!
+//! ## Overview
+//!
+//! The Collator Selection pallet manages the collators of a parachain. **Collation is _not_ a
+//! secure activity** and this pallet does not implement any game-theoretic mechanisms to meet BFT
+//! safety assumptions of the chosen set.
+//!
+//! ## Terminology
+//!
+//! - Collator: A parachain block producer.
+//! - Bond: An amount of `Balance` _reserved_ for candidate registration.
+//! - Invulnerable: An account guaranteed to be in the collator set.
+//!
+//! ## Implementation
+//!
+//! The final `Collators` are aggregated from two individual lists:
+//!
+//! 1. [`Invulnerables`]: a set of collators appointed by governance. These accounts will always be
+//!    collators.
+//! 2. [`Candidates`]: these are *candidates to the collation task* and may or may not be elected as
+//!    a final collator.
+//!
+//! The current implementation resolves congestion of [`Candidates`] in a first-come-first-serve
+//! manner.
+//!
+//! Candidates will not be allowed to get kicked or leave_intent if the total number of candidates
+//! fall below MinCandidates. This is for potential disaster recovery scenarios.
+//!
+//! ### Rewards
+//!
+//! The Collator Selection pallet maintains an on-chain account (the "Pot"). In each block, the
+//! collator who authored it receives:
+//!
+//! - Half the value of the Pot.
+//! - Half the value of the transaction fees within the block. The other half of the transaction
+//!   fees are deposited into the Pot.
+//!
+//! To initiate rewards an ED needs to be transferred to the pot address.
+//!
+//! Note: Eventually the Pot distribution may be modified as discussed in
+//! [this issue](https://github.com/paritytech/statemint/issues/21#issuecomment-810481073).
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+pub use pallet::*;
+
+#[cfg(test)]
+mod mock;
+
+#[cfg(test)]
+mod tests;
+
+#[cfg(feature = "runtime-benchmarks")]
+mod benchmarking;
+pub mod weights;
+
+#[frame_support::pallet]
+pub mod pallet {
+	pub use crate::weights::WeightInfo;
+	use core::ops::Div;
+	use frame_support::{
+		dispatch::{DispatchClass, DispatchResultWithPostInfo},
+		inherent::Vec,
+		pallet_prelude::*,
+		sp_runtime::traits::{AccountIdConversion, CheckedSub, Saturating, Zero},
+		traits::{
+			Currency, EnsureOrigin, ExistenceRequirement::KeepAlive, ReservableCurrency,
+			ValidatorRegistration,
+		},
+		BoundedVec, PalletId,
+	};
+	use frame_system::pallet_prelude::*;
+	use pallet_session::SessionManager;
+	use sp_runtime::{Perbill, traits::Convert};
+	use pallet_configuration::{
+		CollatorSelectionDesiredCollatorsOverride as DesiredCollators,
+		CollatorSelectionLicenseBondOverride as LicenseBond,
+		CollatorSelectionKickThresholdOverride as KickThreshold, BalanceOf,
+	};
+	use sp_staking::SessionIndex;
+
+	/// A convertor from collators id. Since this pallet does not have stash/controller, this is
+	/// just identity.
+	pub struct IdentityCollator;
+	impl<T> sp_runtime::traits::Convert<T, Option<T>> for IdentityCollator {
+		fn convert(t: T) -> Option<T> {
+			Some(t)
+		}
+	}
+
+	/// Configure the pallet by specifying the parameters and types on which it depends.
+	#[pallet::config]
+	pub trait Config: frame_system::Config + pallet_configuration::Config {
+		/// Overarching event type.
+		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
+
+		/// Origin that can dictate updating parameters of this pallet.
+		type UpdateOrigin: EnsureOrigin<Self::RuntimeOrigin>;
+
+		/// Account Identifier that holds the chain's treasury.
+		type TreasuryAccountId: Get<Self::AccountId>;
+
+		/// Account Identifier from which the internal Pot is generated.
+		type PotId: Get<PalletId>;
+
+		/// Maximum number of candidates and invulnerables that we should have. This is enforced in code.
+		type MaxCollators: Get<u32>;
+
+		/// If kicked, how much of the collator's deposit will be slashed and sent to the slash destination.
+		type SlashRatio: Get<Perbill>;
+
+		/// A stable ID for a validator.
+		type ValidatorId: Member + Parameter;
+
+		/// A conversion from account ID to validator ID.
+		///
+		/// Its cost must be at most one storage read.
+		type ValidatorIdOf: Convert<Self::AccountId, Option<Self::ValidatorId>>;
+
+		/// Validate a user is registered
+		type ValidatorRegistration: ValidatorRegistration<Self::ValidatorId>;
+
+		/// The weight information of this pallet.
+		type WeightInfo: WeightInfo;
+	}
+
+	#[pallet::pallet]
+	#[pallet::generate_store(pub(super) trait Store)]
+	pub struct Pallet<T>(_);
+
+	/// The invulnerable, fixed collators.
+	#[pallet::storage]
+	#[pallet::getter(fn invulnerables)]
+	pub type Invulnerables<T: Config> =
+		StorageValue<_, BoundedVec<T::AccountId, T::MaxCollators>, ValueQuery>;
+
+	/// The (community) collation license holders.
+	#[pallet::storage]
+	#[pallet::getter(fn license_deposit_of)]
+	pub type LicenseDepositOf<T: Config> =
+		StorageMap<_, Blake2_128Concat, T::AccountId, BalanceOf<T>, ValueQuery>;
+
+	/// The (community, limited) collation candidates.
+	#[pallet::storage]
+	#[pallet::getter(fn candidates)]
+	pub type Candidates<T: Config> =
+		StorageValue<_, BoundedVec<T::AccountId, T::MaxCollators>, ValueQuery>;
+
+	/// Last block authored by collator.
+	#[pallet::storage]
+	#[pallet::getter(fn last_authored_block)]
+	pub type LastAuthoredBlock<T: Config> =
+		StorageMap<_, Twox64Concat, T::AccountId, T::BlockNumber, ValueQuery>;
+
+	#[pallet::genesis_config]
+	pub struct GenesisConfig<T: Config> {
+		pub invulnerables: Vec<T::AccountId>,
+	}
+
+	#[cfg(feature = "std")]
+	impl<T: Config> Default for GenesisConfig<T> {
+		fn default() -> Self {
+			Self {
+				invulnerables: Default::default(),
+			}
+		}
+	}
+
+	#[pallet::genesis_build]
+	impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {
+		fn build(&self) {
+			let duplicate_invulnerables = self
+				.invulnerables
+				.iter()
+				.collect::<std::collections::BTreeSet<_>>();
+			assert!(
+				duplicate_invulnerables.len() == self.invulnerables.len(),
+				"duplicate invulnerables in genesis."
+			);
+
+			let bounded_invulnerables =
+				BoundedVec::<_, T::MaxCollators>::try_from(self.invulnerables.clone())
+					.expect("genesis invulnerables are more than T::MaxCollators");
+
+			<Invulnerables<T>>::put(bounded_invulnerables);
+		}
+	}
+
+	#[pallet::event]
+	#[pallet::generate_deposit(pub(super) fn deposit_event)]
+	pub enum Event<T: Config> {
+		InvulnerableAdded {
+			invulnerable: T::AccountId,
+		},
+		InvulnerableRemoved {
+			invulnerable: T::AccountId,
+		},
+		LicenseObtained {
+			account_id: T::AccountId,
+			deposit: BalanceOf<T>,
+		},
+		LicenseReleased {
+			account_id: T::AccountId,
+			deposit_returned: BalanceOf<T>,
+		},
+		CandidateAdded {
+			account_id: T::AccountId,
+		},
+		CandidateRemoved {
+			account_id: T::AccountId,
+		},
+	}
+
+	// Errors inform users that something went wrong.
+	#[pallet::error]
+	pub enum Error<T> {
+		/// Too many candidates
+		TooManyCandidates,
+		/// Unknown error
+		Unknown,
+		/// Permission issue
+		Permission,
+		/// User already holds license to collate
+		AlreadyHoldingLicense,
+		/// User does not hold a license to collate
+		NoLicense,
+		/// User is already a candidate
+		AlreadyCandidate,
+		/// User is not a candidate
+		NotCandidate,
+		/// Too many invulnerables
+		TooManyInvulnerables,
+		/// Too few invulnerables
+		TooFewInvulnerables,
+		/// User is already an Invulnerable
+		AlreadyInvulnerable,
+		/// User is not an Invulnerable
+		NotInvulnerable,
+		/// Account has no associated validator ID
+		NoAssociatedValidatorId,
+		/// Validator ID is not yet registered
+		ValidatorNotRegistered,
+	}
+
+	#[pallet::hooks]
+	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
+
+	#[pallet::call]
+	impl<T: Config> Pallet<T> {
+		/// Add a collator to the list of invulnerable (fixed) collators.
+		#[pallet::call_index(0)]
+		#[pallet::weight(<T as Config>::WeightInfo::add_invulnerable(T::MaxCollators::get()))]
+		pub fn add_invulnerable(
+			origin: OriginFor<T>,
+			new: T::AccountId,
+		) -> DispatchResultWithPostInfo {
+			T::UpdateOrigin::ensure_origin(origin)?;
+
+			// check if the new invulnerable has associated validator keys before it is added
+			let validator_key = T::ValidatorIdOf::convert(new.clone())
+				.ok_or(Error::<T>::NoAssociatedValidatorId)?;
+			ensure!(
+				T::ValidatorRegistration::is_registered(&validator_key),
+				Error::<T>::ValidatorNotRegistered
+			);
+			if Self::invulnerables().contains(&new) {
+				return Ok(().into());
+			}
+
+			<Invulnerables<T>>::try_append(new.clone())
+				.map_err(|_| Error::<T>::TooManyInvulnerables)?;
+
+			// try to offboard the new invulnerable if it was a collator candidate before
+			let _ = Self::try_remove_candidate(&new);
+
+			Self::deposit_event(Event::InvulnerableAdded { invulnerable: new });
+			Ok(().into())
+		}
+
+		/// Remove a collator from the list of invulnerable (fixed) collators.
+		#[pallet::call_index(1)]
+		#[pallet::weight(<T as Config>::WeightInfo::remove_invulnerable(T::MaxCollators::get()))]
+		pub fn remove_invulnerable(
+			origin: OriginFor<T>,
+			who: T::AccountId,
+		) -> DispatchResultWithPostInfo {
+			T::UpdateOrigin::ensure_origin(origin)?;
+
+			<Invulnerables<T>>::try_mutate(|invulnerables| -> DispatchResult {
+				if invulnerables.len() <= 1 {
+					return Err(Error::<T>::TooFewInvulnerables.into());
+				}
+
+				let index = invulnerables
+					.into_iter()
+					.position(|r| *r == who)
+					.ok_or(Error::<T>::NotInvulnerable)?;
+				invulnerables.remove(index);
+				Ok(())
+			})?;
+			Self::deposit_event(Event::InvulnerableRemoved { invulnerable: who });
+			Ok(().into())
+		}
+
+		/// Purchase a license on block collation for this account.
+		/// It does not make it a collator candidate, use `onboard` afterward. The account must
+		/// (a) already have registered session keys and (b) be able to reserve the `LicenseBond`.
+		///
+		/// This call is not available to `Invulnerable` collators.
+		#[pallet::call_index(2)]
+		#[pallet::weight(<T as Config>::WeightInfo::get_license(T::MaxCollators::get()))]
+		pub fn get_license(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
+			// register_as_candidate
+			let who = ensure_signed(origin)?;
+
+			if LicenseDepositOf::<T>::contains_key(&who) {
+				return Err(Error::<T>::AlreadyHoldingLicense.into());
+			}
+
+			let validator_key = T::ValidatorIdOf::convert(who.clone())
+				.ok_or(Error::<T>::NoAssociatedValidatorId)?;
+			ensure!(
+				T::ValidatorRegistration::is_registered(&validator_key),
+				Error::<T>::ValidatorNotRegistered
+			);
+
+			let deposit = <LicenseBond<T>>::get();
+
+			T::Currency::reserve(&who, deposit)?;
+			LicenseDepositOf::<T>::insert(who.clone(), deposit);
+
+			Self::deposit_event(Event::LicenseObtained {
+				account_id: who,
+				deposit,
+			});
+			Ok(().into()) // Some(T::WeightInfo::register_as_candidate(current_count as u32)).into())
+		}
+
+		/// Register this account as a candidate for collators for next sessions.
+		/// The account must already hold a license, and cannot offboard immediately during a session.
+		///
+		/// This call is not available to `Invulnerable` collators.
+		#[pallet::call_index(3)]
+		#[pallet::weight(<T as Config>::WeightInfo::onboard(T::MaxCollators::get()))]
+		pub fn onboard(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
+			// register_as_candidate
+			let who = ensure_signed(origin)?;
+
+			// ensure the user obtained the license.
+			ensure!(
+				LicenseDepositOf::<T>::contains_key(&who),
+				Error::<T>::NoLicense
+			);
+			// ensure we are below limit.
+			let length = <Candidates<T>>::decode_len().unwrap_or_default()
+				+ <Invulnerables<T>>::decode_len().unwrap_or_default();
+			ensure!(
+				(length as u32) < <DesiredCollators<T>>::get(),
+				Error::<T>::TooManyCandidates
+			);
+			ensure!(
+				!Self::invulnerables().contains(&who),
+				Error::<T>::AlreadyInvulnerable
+			);
+
+			let current_count =
+				<Candidates<T>>::try_mutate(|candidates| -> Result<usize, DispatchError> {
+					if candidates.iter().any(|candidate| *candidate == who) {
+						Err(Error::<T>::AlreadyCandidate)?
+					} else {
+						candidates
+							.try_push(who.clone())
+							.map_err(|_| Error::<T>::TooManyCandidates)?;
+						// First authored block is current block plus kick threshold to handle session delay
+						<LastAuthoredBlock<T>>::insert(
+							who.clone(),
+							frame_system::Pallet::<T>::block_number() + <KickThreshold<T>>::get(),
+						);
+						Ok(candidates.len())
+					}
+				})?;
+
+			Self::deposit_event(Event::CandidateAdded { account_id: who });
+			Ok(Some(<T as Config>::WeightInfo::onboard(current_count as u32)).into())
+		}
+
+		/// Deregister `origin` as a collator candidate. Note that the collator can only leave on
+		/// session change. The license to `onboard` later at any other time will remain.
+		#[pallet::call_index(4)]
+		#[pallet::weight(<T as Config>::WeightInfo::offboard(T::MaxCollators::get()))]
+		pub fn offboard(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
+			// leave_intent
+			let who = ensure_signed(origin)?;
+			let current_count = Self::try_remove_candidate(&who)?;
+
+			Ok(Some(<T as Config>::WeightInfo::offboard(current_count as u32)).into())
+		}
+
+		/// Forfeit `origin`'s own license. The `LicenseBond` will be unreserved immediately.
+		///
+		/// This call is not available to `Invulnerable` collators.
+		#[pallet::call_index(5)]
+		#[pallet::weight(<T as Config>::WeightInfo::release_license(T::MaxCollators::get()))]
+		pub fn release_license(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
+			// leave_intent
+			let who = ensure_signed(origin)?;
+
+			let current_count = Self::try_remove_candidate_and_release_license(&who, false, true)?;
+
+			Ok(Some(<T as Config>::WeightInfo::release_license(
+				current_count as u32,
+			))
+			.into())
+		}
+
+		/// Force deregister `origin` as a collator candidate as a governing authority, and revoke its license.
+		/// Note that the collator can only leave on session change.
+		/// The `LicenseBond` will be unreserved and returned immediately.
+		///
+		/// This call is, of course, not applicable to `Invulnerable` collators.
+		#[pallet::call_index(6)]
+		#[pallet::weight(<T as Config>::WeightInfo::force_release_license(T::MaxCollators::get()))]
+		pub fn force_release_license(
+			origin: OriginFor<T>,
+			who: T::AccountId,
+		) -> DispatchResultWithPostInfo {
+			// leave_intent
+			T::UpdateOrigin::ensure_origin(origin)?;
+
+			let current_count = Self::try_remove_candidate_and_release_license(&who, false, true)?;
+
+			Ok(Some(<T as Config>::WeightInfo::force_release_license(
+				current_count as u32,
+			))
+			.into())
+		}
+	}
+
+	impl<T: Config> Pallet<T> {
+		/// Get a unique, inaccessible account id from the `PotId`.
+		pub fn account_id() -> T::AccountId {
+			T::PotId::get().into_account_truncating()
+		}
+
+		/// Removes a candidate and their license, optionally slashed and optionally ignoring,
+		/// whether or not they actually are a candidate.
+		fn try_remove_candidate_and_release_license(
+			who: &T::AccountId,
+			should_slash: bool,
+			ignore_if_not_candidate: bool,
+		) -> Result<usize, DispatchError> {
+			let current_count = Self::try_remove_candidate(who);
+			let current_count = if ignore_if_not_candidate
+				&& current_count == Err(Error::<T>::NotCandidate.into())
+			{
+				<Candidates<T>>::decode_len().unwrap_or_default()
+			} else {
+				current_count?
+			};
+			Self::try_release_license(who, should_slash)?;
+			Ok(current_count)
+		}
+
+		/// Removes a candidate from the collator pool for the next session if they exist.
+		fn try_remove_candidate(who: &T::AccountId) -> Result<usize, DispatchError> {
+			let current_count =
+				<Candidates<T>>::try_mutate(|candidates| -> Result<usize, DispatchError> {
+					let index = candidates
+						.iter()
+						.position(|candidate| *candidate == *who)
+						.ok_or(Error::<T>::NotCandidate)?;
+					candidates.remove(index);
+					<LastAuthoredBlock<T>>::remove(who.clone());
+					Ok(candidates.len())
+				})?;
+			Self::deposit_event(Event::CandidateRemoved {
+				account_id: who.clone(),
+			});
+			Ok(current_count)
+		}
+
+		/// Removes a candidate if they exist and sends them back their deposit, optionally slashed.
+		fn try_release_license(who: &T::AccountId, should_slash: bool) -> DispatchResult {
+			let mut deposit_returned = BalanceOf::<T>::default();
+			LicenseDepositOf::<T>::try_mutate_exists(who, |deposit| -> DispatchResult {
+				if let Some(deposit) = deposit.take() {
+					if should_slash {
+						let slashed = T::SlashRatio::get() * deposit;
+						let remaining = deposit - slashed;
+
+						let (imbalance, _) = T::Currency::slash_reserved(who, slashed);
+						//T::Currency::unreserve(who, remaining);
+						deposit_returned = remaining;
+
+						T::Currency::resolve_creating(&T::TreasuryAccountId::get(), imbalance);
+					} else {
+						//T::Currency::unreserve(who, deposit);
+						deposit_returned = deposit;
+					}
+
+					T::Currency::unreserve(who, deposit_returned);
+					Ok(())
+				} else {
+					Err(Error::<T>::NoLicense.into())
+				}
+			})?;
+			Self::deposit_event(Event::LicenseReleased {
+				account_id: who.clone(),
+				deposit_returned,
+			});
+			Ok(())
+		}
+
+		/// Assemble the current set of candidates and invulnerables into the next collator set.
+		///
+		/// This is done on the fly, as frequent as we are told to do so, as the session manager.
+		pub fn assemble_collators(
+			candidates: BoundedVec<T::AccountId, T::MaxCollators>,
+		) -> Vec<T::AccountId> {
+			let mut collators = Self::invulnerables().to_vec();
+			collators.extend(candidates);
+			collators
+		}
+
+		/// Kicks out candidates that did not produce a block in the kick threshold
+		/// and **confiscates** their deposits to the treasury.
+		pub fn kick_stale_candidates(
+			candidates: BoundedVec<T::AccountId, T::MaxCollators>,
+		) -> BoundedVec<T::AccountId, T::MaxCollators> {
+			let now = frame_system::Pallet::<T>::block_number();
+			let kick_threshold = <KickThreshold<T>>::get();
+			candidates
+				.into_iter()
+				.filter_map(|c| {
+					let last_block = <LastAuthoredBlock<T>>::get(c.clone());
+					let since_last = now.saturating_sub(last_block);
+					if since_last < kick_threshold {
+						Some(c)
+					} else {
+						let outcome = Self::try_remove_candidate_and_release_license(&c, true, false);
+						if let Err(why) = outcome {
+							log::warn!("Failed to kick collator and release license {:?}", why);
+							debug_assert!(false, "failed to kick collator and release license {why:?}");
+						}
+						None
+					}
+				})
+				.collect::<Vec<_>>()
+				.try_into()
+				.expect("filter_map operation can't result in a bounded vec larger than its original; qed")
+		}
+	}
+
+	/// Keep track of number of authored blocks per authority, uncles are counted as well since
+	/// they're a valid proof of being online.
+	impl<T: Config + pallet_authorship::Config>
+		pallet_authorship::EventHandler<T::AccountId, T::BlockNumber> for Pallet<T>
+	{
+		fn note_author(author: T::AccountId) {
+			let pot = Self::account_id();
+			// assumes an ED will be sent to pot.
+			let reward = T::Currency::free_balance(&pot)
+				.checked_sub(&T::Currency::minimum_balance())
+				.unwrap_or_else(Zero::zero)
+				.div(2u32.into());
+			// `reward` is half of pot account minus ED, this should never fail.
+			let _success = T::Currency::transfer(&pot, &author, reward, KeepAlive);
+			debug_assert!(_success.is_ok());
+			<LastAuthoredBlock<T>>::insert(author, frame_system::Pallet::<T>::block_number());
+
+			frame_system::Pallet::<T>::register_extra_weight_unchecked(
+				<T as Config>::WeightInfo::note_author(),
+				DispatchClass::Mandatory,
+			);
+		}
+
+		fn note_uncle(_author: T::AccountId, _age: T::BlockNumber) {
+			//TODO can we ignore this?
+		}
+	}
+
+	/// Play the role of the session manager.
+	impl<T: Config> SessionManager<T::AccountId> for Pallet<T> {
+		fn new_session(index: SessionIndex) -> Option<Vec<T::AccountId>> {
+			log::info!(
+				"assembling new collators for new session {} at #{:?}",
+				index,
+				<frame_system::Pallet<T>>::block_number(),
+			);
+
+			let candidates = Self::candidates();
+			let candidates_len_before = candidates.len();
+			let active_candidates = Self::kick_stale_candidates(candidates);
+			let removed = candidates_len_before - active_candidates.len();
+			let result = Self::assemble_collators(active_candidates);
+
+			frame_system::Pallet::<T>::register_extra_weight_unchecked(
+				<T as Config>::WeightInfo::new_session(
+					candidates_len_before as u32,
+					removed as u32,
+				),
+				DispatchClass::Mandatory,
+			);
+			Some(result)
+		}
+		fn start_session(_: SessionIndex) {
+			// we don't care.
+		}
+		fn end_session(_: SessionIndex) {
+			// we don't care.
+		}
+	}
+}
addedpallets/collator-selection/src/mock.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/collator-selection/src/mock.rs
@@ -0,0 +1,302 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+// Original license:
+// Copyright (C) 2021 Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use super::*;
+use crate as collator_selection;
+use frame_support::{
+	ord_parameter_types, parameter_types,
+	traits::{FindAuthor, GenesisBuild, ValidatorRegistration},
+	PalletId,
+};
+use frame_system as system;
+use frame_system::EnsureSignedBy;
+use sp_core::H256;
+use sp_runtime::{
+	testing::{Header, UintAuthorityId},
+	traits::{BlakeTwo256, IdentityLookup, OpaqueKeys},
+	Perbill, RuntimeAppPublic,
+};
+
+type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
+type Block = frame_system::mocking::MockBlock<Test>;
+
+// Configure a mock runtime to test the pallet.
+frame_support::construct_runtime!(
+	pub enum Test where
+		Block = Block,
+		NodeBlock = Block,
+		UncheckedExtrinsic = UncheckedExtrinsic,
+	{
+		System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
+		Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent},
+		Session: pallet_session::{Pallet, Call, Storage, Event, Config<T>},
+		Aura: pallet_aura::{Pallet, Storage, Config<T>},
+		Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
+		CollatorSelection: collator_selection::{Pallet, Call, Storage, Event<T>},
+		Authorship: pallet_authorship::{Pallet, Call, Storage, Inherent},
+		Configuration: pallet_configuration::{Pallet, Call, Storage, Event<T>},
+	}
+);
+
+parameter_types! {
+	pub const BlockHashCount: u64 = 250;
+	pub const SS58Prefix: u8 = 42;
+}
+
+impl system::Config for Test {
+	type BaseCallFilter = frame_support::traits::Everything;
+	type BlockWeights = ();
+	type BlockLength = ();
+	type DbWeight = ();
+	type RuntimeOrigin = RuntimeOrigin;
+	type RuntimeCall = RuntimeCall;
+	type Index = u64;
+	type BlockNumber = u64;
+	type Hash = H256;
+	type Hashing = BlakeTwo256;
+	type AccountId = u64;
+	type Lookup = IdentityLookup<Self::AccountId>;
+	type Header = Header;
+	type RuntimeEvent = RuntimeEvent;
+	type BlockHashCount = BlockHashCount;
+	type Version = ();
+	type PalletInfo = PalletInfo;
+	type AccountData = pallet_balances::AccountData<u64>;
+	type OnNewAccount = ();
+	type OnKilledAccount = ();
+	type SystemWeightInfo = ();
+	type SS58Prefix = SS58Prefix;
+	type OnSetCode = ();
+	type MaxConsumers = frame_support::traits::ConstU32<16>;
+}
+
+parameter_types! {
+	pub const ExistentialDeposit: u64 = 5;
+	pub const MaxReserves: u32 = 50;
+}
+
+impl pallet_balances::Config for Test {
+	type Balance = u64;
+	type RuntimeEvent = RuntimeEvent;
+	type DustRemoval = ();
+	type ExistentialDeposit = ExistentialDeposit;
+	type AccountStore = System;
+	type WeightInfo = ();
+	type MaxLocks = ();
+	type MaxReserves = MaxReserves;
+	type ReserveIdentifier = [u8; 8];
+}
+
+pub struct Author4;
+impl FindAuthor<u64> for Author4 {
+	fn find_author<'a, I>(_digests: I) -> Option<u64>
+	where
+		I: 'a + IntoIterator<Item = (frame_support::ConsensusEngineId, &'a [u8])>,
+	{
+		Some(4)
+	}
+}
+
+impl pallet_authorship::Config for Test {
+	type FindAuthor = Author4;
+	type UncleGenerations = ();
+	type FilterUncle = ();
+	type EventHandler = CollatorSelection;
+}
+
+parameter_types! {
+	pub const MinimumPeriod: u64 = 1;
+}
+
+impl pallet_timestamp::Config for Test {
+	type Moment = u64;
+	type OnTimestampSet = Aura;
+	type MinimumPeriod = MinimumPeriod;
+	type WeightInfo = ();
+}
+
+impl pallet_aura::Config for Test {
+	type AuthorityId = sp_consensus_aura::sr25519::AuthorityId;
+	type MaxAuthorities = MaxAuthorities;
+	type DisabledValidators = ();
+}
+
+sp_runtime::impl_opaque_keys! {
+	pub struct MockSessionKeys {
+		// a key for aura authoring
+		pub aura: UintAuthorityId,
+	}
+}
+
+impl From<UintAuthorityId> for MockSessionKeys {
+	fn from(aura: sp_runtime::testing::UintAuthorityId) -> Self {
+		Self { aura }
+	}
+}
+
+parameter_types! {
+	pub static SessionHandlerCollators: Vec<u64> = Vec::new();
+	pub static SessionChangeBlock: u64 = 0;
+}
+
+pub struct TestSessionHandler;
+impl pallet_session::SessionHandler<u64> for TestSessionHandler {
+	const KEY_TYPE_IDS: &'static [sp_runtime::KeyTypeId] = &[UintAuthorityId::ID];
+	fn on_genesis_session<Ks: OpaqueKeys>(keys: &[(u64, Ks)]) {
+		SessionHandlerCollators::set(keys.into_iter().map(|(a, _)| *a).collect::<Vec<_>>())
+	}
+	fn on_new_session<Ks: OpaqueKeys>(_: bool, keys: &[(u64, Ks)], _: &[(u64, Ks)]) {
+		SessionChangeBlock::set(System::block_number());
+		dbg!(keys.len());
+		SessionHandlerCollators::set(keys.into_iter().map(|(a, _)| *a).collect::<Vec<_>>())
+	}
+	fn on_before_session_ending() {}
+	fn on_disabled(_: u32) {}
+}
+
+parameter_types! {
+	pub const Offset: u64 = 0;
+	pub const Period: u64 = 10;
+}
+
+impl pallet_session::Config for Test {
+	type RuntimeEvent = RuntimeEvent;
+	type ValidatorId = <Self as frame_system::Config>::AccountId;
+	// we don't have stash and controller, thus we don't need the convert as well.
+	type ValidatorIdOf = IdentityCollator;
+	type ShouldEndSession = pallet_session::PeriodicSessions<Period, Offset>;
+	type NextSessionRotation = pallet_session::PeriodicSessions<Period, Offset>;
+	type SessionManager = CollatorSelection;
+	type SessionHandler = TestSessionHandler;
+	type Keys = MockSessionKeys;
+	type WeightInfo = ();
+}
+
+parameter_types! {
+	pub const MaxCollators: u32 = 5;
+	pub const LicenseBond: u64 = 10;
+	pub const KickThreshold: u64 = 10;
+	// the following values do not matter and are meaningless, etc.
+	pub const DefaultWeightToFeeCoefficient: u64 = 100_000;
+	pub const DefaultMinGasPrice: u64 = 100_000;
+	pub const MaxXcmAllowedLocations: u32 = 16;
+	pub AppPromotionDailyRate: Perbill = Perbill::from_rational(5u32, 10_000);
+	pub const DayRelayBlocks: u32 = 1;
+}
+
+impl pallet_configuration::Config for Test {
+	type RuntimeEvent = RuntimeEvent;
+	type Currency = Balances;
+	type DefaultCollatorSelectionMaxCollators = MaxCollators;
+	type DefaultCollatorSelectionKickThreshold = KickThreshold;
+	type DefaultCollatorSelectionLicenseBond = LicenseBond;
+	// the following constants we don't care about
+	type DefaultWeightToFeeCoefficient = DefaultWeightToFeeCoefficient;
+	type DefaultMinGasPrice = DefaultMinGasPrice;
+	type MaxXcmAllowedLocations = MaxXcmAllowedLocations;
+	type AppPromotionDailyRate = AppPromotionDailyRate;
+	type DayRelayBlocks = DayRelayBlocks;
+	type WeightInfo = pallet_configuration::weights::SubstrateWeight<Self>;
+}
+
+ord_parameter_types! {
+	pub const RootAccount: u64 = 777;
+}
+
+parameter_types! {
+	pub const PotId: PalletId = PalletId(*b"PotStake");
+	pub const MaxAuthorities: u32 = 100_000;
+	pub const SlashRatio: Perbill = Perbill::one();
+}
+
+pub struct IsRegistered;
+impl ValidatorRegistration<u64> for IsRegistered {
+	fn is_registered(id: &u64) -> bool {
+		if *id == 7u64 {
+			false
+		} else {
+			true
+		}
+	}
+}
+
+impl Config for Test {
+	type RuntimeEvent = RuntimeEvent;
+	type UpdateOrigin = EnsureSignedBy<RootAccount, u64>;
+	type PotId = PotId;
+	type MaxCollators = MaxCollators;
+	type SlashRatio = SlashRatio;
+	type TreasuryAccountId = ();
+	type ValidatorId = <Self as frame_system::Config>::AccountId;
+	type ValidatorIdOf = IdentityCollator;
+	type ValidatorRegistration = IsRegistered;
+	type WeightInfo = ();
+}
+
+pub fn new_test_ext() -> sp_io::TestExternalities {
+	sp_tracing::try_init_simple();
+	let mut t = frame_system::GenesisConfig::default()
+		.build_storage::<Test>()
+		.unwrap();
+	let invulnerables = vec![1, 2];
+
+	let balances = vec![(1, 100), (2, 100), (3, 100), (4, 100), (5, 100)];
+	let keys = balances
+		.iter()
+		.map(|&(i, _)| {
+			(
+				i,
+				i,
+				MockSessionKeys {
+					aura: UintAuthorityId(i),
+				},
+			)
+		})
+		.collect::<Vec<_>>();
+	let collator_selection = collator_selection::GenesisConfig::<Test> { invulnerables };
+	let session = pallet_session::GenesisConfig::<Test> { keys };
+	pallet_balances::GenesisConfig::<Test> { balances }
+		.assimilate_storage(&mut t)
+		.unwrap();
+	// collator selection must be initialized before session.
+	collator_selection.assimilate_storage(&mut t).unwrap();
+	session.assimilate_storage(&mut t).unwrap();
+
+	t.into()
+}
+
+pub fn initialize_to_block(n: u64) {
+	for i in System::block_number() + 1..=n {
+		System::set_block_number(i);
+		<AllPalletsWithSystem as frame_support::traits::OnInitialize<u64>>::on_initialize(i);
+	}
+}
addedpallets/collator-selection/src/tests.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/collator-selection/src/tests.rs
@@ -0,0 +1,538 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+// Original license:
+// Copyright (C) 2021 Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate as collator_selection;
+use crate::{mock::*, Error};
+use frame_support::{
+	assert_noop, assert_ok,
+	traits::{Currency, GenesisBuild, OnInitialize},
+};
+use frame_system::RawOrigin;
+use pallet_balances::Error as BalancesError;
+use sp_runtime::traits::BadOrigin;
+use pallet_configuration::{
+	CollatorSelectionDesiredCollatorsOverride as DesiredCollators,
+	CollatorSelectionKickThresholdOverride as KickThreshold,
+	CollatorSelectionLicenseBondOverride as LicenseBond,
+};
+
+fn get_license_and_onboard(account_id: <Test as frame_system::Config>::AccountId) {
+	assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(
+		account_id
+	)));
+	assert_ok!(CollatorSelection::onboard(RuntimeOrigin::signed(
+		account_id
+	)));
+}
+
+#[test]
+fn basic_setup_works() {
+	new_test_ext().execute_with(|| {
+		assert_eq!(<DesiredCollators<Test>>::get(), 5);
+		assert_eq!(<LicenseBond<Test>>::get(), 10);
+
+		assert!(CollatorSelection::candidates().is_empty());
+		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
+	});
+}
+
+#[test]
+fn it_should_add_invulnerables() {
+	new_test_ext().execute_with(|| {
+		assert_ok!(CollatorSelection::add_invulnerable(
+			RuntimeOrigin::signed(RootAccount::get()),
+			1
+		));
+		assert_ok!(CollatorSelection::add_invulnerable(
+			RuntimeOrigin::signed(RootAccount::get()),
+			2
+		));
+		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
+
+		// cannot set with non-root.
+		assert_noop!(
+			CollatorSelection::add_invulnerable(RuntimeOrigin::signed(1), 3),
+			BadOrigin
+		);
+
+		// cannot set invulnerables without associated validator keys
+		assert_noop!(
+			CollatorSelection::add_invulnerable(RuntimeOrigin::signed(RootAccount::get()), 7),
+			Error::<Test>::ValidatorNotRegistered
+		);
+	});
+}
+
+#[test]
+fn it_should_remove_invulnerables() {
+	new_test_ext().execute_with(|| {
+		assert_ok!(CollatorSelection::add_invulnerable(
+			RuntimeOrigin::signed(RootAccount::get()),
+			1
+		));
+		assert_ok!(CollatorSelection::add_invulnerable(
+			RuntimeOrigin::signed(RootAccount::get()),
+			2
+		));
+
+		// cannot remove with non-root.
+		assert_noop!(
+			CollatorSelection::remove_invulnerable(RuntimeOrigin::signed(1), 3),
+			BadOrigin
+		);
+
+		assert_ok!(CollatorSelection::remove_invulnerable(
+			RuntimeOrigin::signed(RootAccount::get()),
+			2
+		));
+		assert_eq!(CollatorSelection::invulnerables(), vec![1]);
+
+		// cannot remove an invulnerable if there would be 0 invulnerables.
+		assert_noop!(
+			CollatorSelection::remove_invulnerable(RuntimeOrigin::signed(RootAccount::get()), 1),
+			Error::<Test>::TooFewInvulnerables
+		);
+	});
+}
+
+#[test]
+fn set_desired_collators_works() {
+	new_test_ext().execute_with(|| {
+		// given
+		assert_eq!(<DesiredCollators<Test>>::get(), 5);
+
+		// can set
+		assert_ok!(Configuration::set_collator_selection_desired_collators(
+			RawOrigin::Root.into(),
+			Some(7)
+		));
+		assert_eq!(<DesiredCollators<Test>>::get(), 7);
+
+		// rejects bad origin
+		assert_noop!(
+			Configuration::set_collator_selection_desired_collators(
+				RuntimeOrigin::signed(1),
+				Some(8)
+			),
+			BadOrigin
+		);
+	});
+}
+
+#[test]
+fn set_license_bond() {
+	new_test_ext().execute_with(|| {
+		// given
+		assert_eq!(<LicenseBond<Test>>::get(), 10);
+
+		// can set
+		assert_ok!(Configuration::set_collator_selection_license_bond(
+			RawOrigin::Root.into(),
+			Some(7)
+		));
+		assert_eq!(<LicenseBond<Test>>::get(), 7);
+
+		// rejects bad origin.
+		assert_noop!(
+			Configuration::set_collator_selection_license_bond(RuntimeOrigin::signed(1), Some(8)),
+			BadOrigin
+		);
+	});
+}
+
+#[test]
+fn cannot_onboard_candidate_with_no_license() {
+	new_test_ext().execute_with(|| {
+		// can't onboard a candidate who did not get a license.
+		assert_noop!(
+			CollatorSelection::onboard(RuntimeOrigin::signed(3)),
+			Error::<Test>::NoLicense,
+		);
+
+		// but give it a license and welcome aboard.
+		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::onboard(RuntimeOrigin::signed(3)));
+	})
+}
+
+#[test]
+fn cannot_onboard_candidate_if_too_many() {
+	new_test_ext().execute_with(|| {
+		// reset desired candidates
+		<pallet_configuration::CollatorSelectionDesiredCollatorsOverride<Test>>::put(0);
+
+		// can still get a license.
+		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(4)));
+
+		// can't accept anyone anymore.
+		assert_noop!(
+			CollatorSelection::onboard(RuntimeOrigin::signed(4)),
+			Error::<Test>::TooManyCandidates,
+		);
+
+		// reset desired candidates to invulnerables + 1
+		<pallet_configuration::CollatorSelectionDesiredCollatorsOverride<Test>>::put(3);
+		assert_ok!(CollatorSelection::onboard(RuntimeOrigin::signed(4)));
+
+		// but no more.
+		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(5)));
+		assert_noop!(
+			CollatorSelection::onboard(RuntimeOrigin::signed(5)),
+			Error::<Test>::TooManyCandidates,
+		);
+	})
+}
+
+#[test]
+fn cannot_obtain_license_if_keys_not_registered() {
+	new_test_ext().execute_with(|| {
+		// can't 7 because keys not registered.
+		assert_noop!(
+			CollatorSelection::get_license(RuntimeOrigin::signed(7)),
+			Error::<Test>::ValidatorNotRegistered
+		);
+	})
+}
+
+#[test]
+fn cannot_obtain_license_if_poor() {
+	new_test_ext().execute_with(|| {
+		assert_eq!(Balances::free_balance(&3), 100);
+		assert_eq!(Balances::free_balance(&33), 0);
+
+		// works
+		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));
+
+		// poor
+		assert_noop!(
+			CollatorSelection::get_license(RuntimeOrigin::signed(33)),
+			BalancesError::<Test>::InsufficientBalance,
+		);
+	});
+}
+
+#[test]
+fn cannot_onboard_dupe_candidate() {
+	new_test_ext().execute_with(|| {
+		// can add 3 as candidate
+		get_license_and_onboard(3);
+		assert_eq!(CollatorSelection::license_deposit_of(3), 10);
+		assert_eq!(CollatorSelection::candidates(), vec![3]);
+		assert_eq!(CollatorSelection::last_authored_block(3), 10);
+		assert_eq!(Balances::free_balance(3), 90);
+
+		// but no more
+		assert_noop!(
+			CollatorSelection::get_license(RuntimeOrigin::signed(3)),
+			Error::<Test>::AlreadyHoldingLicense,
+		);
+		assert_noop!(
+			CollatorSelection::onboard(RuntimeOrigin::signed(3)),
+			Error::<Test>::AlreadyCandidate,
+		);
+	})
+}
+
+#[test]
+fn becoming_candidate_works() {
+	new_test_ext().execute_with(|| {
+		// given
+		assert_eq!(<DesiredCollators<Test>>::get(), 5);
+		assert_eq!(<LicenseBond<Test>>::get(), 10);
+		assert_eq!(CollatorSelection::candidates(), Vec::new());
+		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
+
+		// take two endowed, non-invulnerables accounts.
+		assert_eq!(Balances::free_balance(&3), 100);
+		assert_eq!(Balances::free_balance(&4), 100);
+
+		get_license_and_onboard(3);
+		get_license_and_onboard(4);
+
+		assert_eq!(Balances::free_balance(&3), 90);
+		assert_eq!(Balances::free_balance(&4), 90);
+
+		assert_eq!(CollatorSelection::candidates().len(), 2);
+	});
+}
+
+#[test]
+fn cannot_become_candidate_if_invulnerable() {
+	new_test_ext().execute_with(|| {
+		assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);
+
+		// can obtain a license even if is invulnerable.
+		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(1)));
+		// but cannot onboard
+		assert_noop!(
+			CollatorSelection::onboard(RuntimeOrigin::signed(1)),
+			Error::<Test>::AlreadyInvulnerable,
+		);
+
+		// get a license and then become invulnerable.
+		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::add_invulnerable(
+			RuntimeOrigin::signed(RootAccount::get()),
+			3
+		));
+		assert_noop!(
+			CollatorSelection::onboard(RuntimeOrigin::signed(3)),
+			Error::<Test>::AlreadyInvulnerable,
+		);
+	})
+}
+
+#[test]
+fn can_become_invulnerable_if_candidate() {
+	new_test_ext().execute_with(|| {
+		// become a candidate and then become invulnerable.
+		get_license_and_onboard(3);
+		assert_eq!(CollatorSelection::candidates(), vec![3]);
+
+		assert_ok!(CollatorSelection::add_invulnerable(
+			RuntimeOrigin::signed(RootAccount::get()),
+			3
+		));
+		// should exclude from candidates, but not revoke the license
+		assert_eq!(CollatorSelection::candidates(), vec![]);
+		assert_eq!(CollatorSelection::license_deposit_of(3), 10);
+		assert_eq!(Balances::free_balance(3), 90);
+	});
+}
+
+#[test]
+fn offboard() {
+	new_test_ext().execute_with(|| {
+		// register a candidate.
+		get_license_and_onboard(3);
+		assert_eq!(Balances::free_balance(3), 90);
+
+		// cannot leave if holds license but not yet candidate.
+		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(4)));
+		assert_noop!(
+			CollatorSelection::offboard(RuntimeOrigin::signed(4)),
+			Error::<Test>::NotCandidate
+		);
+		// cannot leave if does not hold license.
+		assert_noop!(
+			CollatorSelection::offboard(RuntimeOrigin::signed(5)),
+			Error::<Test>::NotCandidate
+		);
+
+		// bond is returned - only after releasing the license
+		assert_ok!(CollatorSelection::offboard(RuntimeOrigin::signed(3)));
+		assert_eq!(Balances::free_balance(3), 90);
+		assert_eq!(CollatorSelection::last_authored_block(3), 0);
+		assert_ok!(CollatorSelection::release_license(RuntimeOrigin::signed(3)));
+		assert_eq!(Balances::free_balance(3), 100);
+	});
+}
+
+#[test]
+fn release_license() {
+	new_test_ext().execute_with(|| {
+		// obtain a license to collate and reserve the bond.
+		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));
+		assert_eq!(Balances::free_balance(3), 90);
+
+		// release the license and get the bond back.
+		assert_ok!(CollatorSelection::release_license(RuntimeOrigin::signed(3)));
+		assert_eq!(Balances::free_balance(3), 100);
+
+		// register a candidate.
+		get_license_and_onboard(3);
+		assert_eq!(Balances::free_balance(3), 90);
+
+		// can release license even if onboarded.
+		assert_ok!(CollatorSelection::release_license(RuntimeOrigin::signed(3)));
+		assert_eq!(Balances::free_balance(3), 100);
+		assert_eq!(CollatorSelection::candidates(), vec![]);
+	});
+}
+
+#[test]
+fn force_release_license() {
+	new_test_ext().execute_with(|| {
+		// obtain a license to collate and reserve the bond.
+		assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));
+		assert_eq!(Balances::free_balance(3), 90);
+
+		// cannot execute the operation as non-root
+		assert_noop!(
+			CollatorSelection::force_release_license(RuntimeOrigin::signed(3), 3),
+			BadOrigin
+		);
+
+		// release the license and get the bond back.
+		assert_ok!(CollatorSelection::force_release_license(
+			RuntimeOrigin::signed(RootAccount::get()),
+			3
+		));
+		assert_eq!(Balances::free_balance(3), 100);
+
+		// register a candidate.
+		get_license_and_onboard(3);
+		assert_eq!(Balances::free_balance(3), 90);
+
+		// can release license even if onboarded.
+		assert_ok!(CollatorSelection::force_release_license(
+			RuntimeOrigin::signed(RootAccount::get()),
+			3
+		));
+		assert_eq!(Balances::free_balance(3), 100);
+		assert_eq!(CollatorSelection::candidates(), vec![]);
+	});
+}
+
+#[test]
+fn authorship_event_handler() {
+	new_test_ext().execute_with(|| {
+		// put 100 in the pot + 5 for ED
+		Balances::make_free_balance_be(&CollatorSelection::account_id(), 105);
+
+		// 4 is the default author.
+		assert_eq!(Balances::free_balance(4), 100);
+		get_license_and_onboard(4);
+		// triggers `note_author`
+		Authorship::on_initialize(1);
+
+		assert_eq!(CollatorSelection::candidates(), vec![4]);
+		assert_eq!(CollatorSelection::last_authored_block(4), 0);
+
+		// half of the pot goes to the collator who's the author (4 in tests).
+		assert_eq!(Balances::free_balance(4), 140);
+		// half + ED stays.
+		assert_eq!(Balances::free_balance(CollatorSelection::account_id()), 55);
+	});
+}
+
+#[test]
+fn fees_edgecases() {
+	new_test_ext().execute_with(|| {
+		// Nothing panics, no reward when no ED in balance
+		Authorship::on_initialize(1);
+		// put some money into the pot at ED
+		Balances::make_free_balance_be(&CollatorSelection::account_id(), 5);
+		// 4 is the default author.
+		assert_eq!(Balances::free_balance(4), 100);
+		get_license_and_onboard(4);
+		// triggers `note_author`
+		Authorship::on_initialize(1);
+
+		assert_eq!(CollatorSelection::candidates(), vec![4]);
+		assert_eq!(CollatorSelection::last_authored_block(4), 0);
+		// Nothing received
+		assert_eq!(Balances::free_balance(4), 90);
+		// all fee stays
+		assert_eq!(Balances::free_balance(CollatorSelection::account_id()), 5);
+	});
+}
+
+#[test]
+fn session_management_works() {
+	new_test_ext().execute_with(|| {
+		initialize_to_block(1);
+
+		assert_eq!(SessionChangeBlock::get(), 0);
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+
+		initialize_to_block(4);
+
+		assert_eq!(SessionChangeBlock::get(), 0);
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+
+		// add a new collator
+		get_license_and_onboard(5);
+
+		// session won't see this.
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+		// but we have a new candidate.
+		assert_eq!(CollatorSelection::candidates().len(), 1);
+
+		initialize_to_block(10);
+		assert_eq!(SessionChangeBlock::get(), 10);
+		// pallet-session has 1 session delay; current validators are the same.
+		assert_eq!(Session::validators(), vec![1, 2]);
+		// queued ones are changed, and now we have 3.
+		assert_eq!(Session::queued_keys().len(), 3);
+		// session handlers (aura, et. al.) cannot see this yet.
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
+
+		initialize_to_block(20);
+		assert_eq!(SessionChangeBlock::get(), 20);
+		// changed are now reflected to session handlers.
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 5]);
+	});
+}
+
+#[test]
+fn kick_mechanism() {
+	new_test_ext().execute_with(|| {
+		// add a new collator
+		get_license_and_onboard(3);
+		get_license_and_onboard(4);
+
+		initialize_to_block(10);
+		assert_eq!(CollatorSelection::candidates().len(), 2);
+
+		initialize_to_block(20);
+		assert_eq!(SessionChangeBlock::get(), 20);
+		// 4 authored this block, gets to stay 3 was kicked
+		assert_eq!(CollatorSelection::candidates().len(), 1);
+		// 3 will be kicked after 1 session delay
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 4]);
+
+		assert_eq!(CollatorSelection::candidates(), vec![4]);
+		assert_eq!(<KickThreshold<Test>>::get(), 10);
+		assert_eq!(CollatorSelection::last_authored_block(4), 20);
+
+		initialize_to_block(30);
+		// 3 gets kicked after 1 session delay
+		assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 4]);
+		// kicked collator gets their funds slashed, the deposit going to treasury
+		assert_eq!(Balances::free_balance(3), 90);
+	});
+}
+
+#[test]
+#[should_panic = "duplicate invulnerables in genesis."]
+fn cannot_set_genesis_value_twice() {
+	sp_tracing::try_init_simple();
+	let mut t = frame_system::GenesisConfig::default()
+		.build_storage::<Test>()
+		.unwrap();
+	let invulnerables = vec![1, 1];
+
+	let collator_selection = collator_selection::GenesisConfig::<Test> { invulnerables };
+	// collator selection must be initialized before session.
+	collator_selection.assimilate_storage(&mut t).unwrap();
+}
addedpallets/collator-selection/src/weights.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/collator-selection/src/weights.rs
@@ -0,0 +1,190 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+// Original license:
+// This file is part of Substrate.
+
+// Copyright (C) 2021 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.
+
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+
+use frame_support::{
+	traits::Get,
+	weights::{constants::RocksDbWeight, Weight},
+};
+use sp_std::marker::PhantomData;
+
+// The weight info trait for `pallet_collator_selection`.
+pub trait WeightInfo {
+	fn add_invulnerable(_b: u32) -> Weight;
+	fn remove_invulnerable(_b: u32) -> Weight;
+	fn get_license(_c: u32) -> Weight;
+	fn onboard(_c: u32) -> Weight;
+	fn offboard(_c: u32) -> Weight;
+	fn release_license(_c: u32) -> Weight;
+	fn force_release_license(_c: u32) -> Weight;
+	fn note_author() -> Weight;
+	fn new_session(_c: u32, _r: u32) -> Weight;
+}
+
+/// Weights for pallet_collator_selection using the Substrate node and recommended hardware.
+pub struct SubstrateWeight<T>(PhantomData<T>);
+impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
+	fn add_invulnerable(b: u32) -> Weight {
+		Weight::from_ref_time(18_563_000 as u64)
+			// Standard Error: 0
+			.saturating_add(Weight::from_ref_time(68_000 as u64).saturating_mul(b as u64))
+			.saturating_add(T::DbWeight::get().writes(1 as u64))
+	}
+	fn remove_invulnerable(b: u32) -> Weight {
+		Weight::from_ref_time(18_563_000 as u64)
+			// Standard Error: 0
+			.saturating_add(Weight::from_ref_time(68_000 as u64).saturating_mul(b as u64))
+			.saturating_add(T::DbWeight::get().writes(1 as u64))
+	}
+	fn get_license(c: u32) -> Weight {
+		Weight::from_ref_time(71_196_000 as u64)
+			// Standard Error: 0
+			.saturating_add(Weight::from_ref_time(198_000 as u64).saturating_mul(c as u64))
+			.saturating_add(T::DbWeight::get().reads(4 as u64))
+			.saturating_add(T::DbWeight::get().writes(2 as u64))
+	}
+	fn onboard(c: u32) -> Weight {
+		Weight::from_ref_time(71_196_000 as u64)
+			// Standard Error: 0
+			.saturating_add(Weight::from_ref_time(198_000 as u64).saturating_mul(c as u64))
+			.saturating_add(T::DbWeight::get().reads(4 as u64))
+			.saturating_add(T::DbWeight::get().writes(2 as u64))
+	}
+	fn offboard(c: u32) -> Weight {
+		Weight::from_ref_time(55_336_000 as u64)
+			// Standard Error: 0
+			.saturating_add(Weight::from_ref_time(151_000 as u64).saturating_mul(c as u64))
+			.saturating_add(T::DbWeight::get().reads(1 as u64))
+			.saturating_add(T::DbWeight::get().writes(2 as u64))
+	}
+	fn release_license(c: u32) -> Weight {
+		Weight::from_ref_time(55_336_000 as u64)
+			// Standard Error: 0
+			.saturating_add(Weight::from_ref_time(151_000 as u64).saturating_mul(c as u64))
+			.saturating_add(T::DbWeight::get().reads(1 as u64))
+			.saturating_add(T::DbWeight::get().writes(2 as u64))
+	}
+	fn force_release_license(c: u32) -> Weight {
+		Weight::from_ref_time(55_336_000 as u64)
+			// Standard Error: 0
+			.saturating_add(Weight::from_ref_time(151_000 as u64).saturating_mul(c as u64))
+			.saturating_add(T::DbWeight::get().reads(1 as u64))
+			.saturating_add(T::DbWeight::get().writes(2 as u64))
+	}
+	fn note_author() -> Weight {
+		Weight::from_ref_time(71_461_000 as u64)
+			.saturating_add(T::DbWeight::get().reads(3 as u64))
+			.saturating_add(T::DbWeight::get().writes(4 as u64))
+	}
+	fn new_session(r: u32, c: u32) -> Weight {
+		Weight::from_ref_time(0 as u64)
+			// Standard Error: 1_010_000
+			.saturating_add(Weight::from_ref_time(109_961_000 as u64).saturating_mul(r as u64))
+			// Standard Error: 1_010_000
+			.saturating_add(Weight::from_ref_time(151_952_000 as u64).saturating_mul(c as u64))
+			.saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(r as u64)))
+			.saturating_add(T::DbWeight::get().reads((2 as u64).saturating_mul(c as u64)))
+			.saturating_add(T::DbWeight::get().writes((2 as u64).saturating_mul(r as u64)))
+			.saturating_add(T::DbWeight::get().writes((2 as u64).saturating_mul(c as u64)))
+	}
+}
+
+// For backwards compatibility and tests
+impl WeightInfo for () {
+	fn add_invulnerable(b: u32) -> Weight {
+		Weight::from_ref_time(18_563_000 as u64)
+			// Standard Error: 0
+			.saturating_add(Weight::from_ref_time(68_000 as u64).saturating_mul(b as u64))
+			.saturating_add(RocksDbWeight::get().writes(1 as u64))
+	}
+	fn remove_invulnerable(b: u32) -> Weight {
+		Weight::from_ref_time(18_563_000 as u64)
+			// Standard Error: 0
+			.saturating_add(Weight::from_ref_time(68_000 as u64).saturating_mul(b as u64))
+			.saturating_add(RocksDbWeight::get().writes(1 as u64))
+	}
+	fn get_license(c: u32) -> Weight {
+		Weight::from_ref_time(71_196_000 as u64)
+			// Standard Error: 0
+			.saturating_add(Weight::from_ref_time(198_000 as u64).saturating_mul(c as u64))
+			.saturating_add(RocksDbWeight::get().reads(4 as u64))
+			.saturating_add(RocksDbWeight::get().writes(2 as u64))
+	}
+	fn onboard(c: u32) -> Weight {
+		Weight::from_ref_time(71_196_000 as u64)
+			// Standard Error: 0
+			.saturating_add(Weight::from_ref_time(198_000 as u64).saturating_mul(c as u64))
+			.saturating_add(RocksDbWeight::get().reads(4 as u64))
+			.saturating_add(RocksDbWeight::get().writes(2 as u64))
+	}
+	fn offboard(c: u32) -> Weight {
+		Weight::from_ref_time(55_336_000 as u64)
+			// Standard Error: 0
+			.saturating_add(Weight::from_ref_time(151_000 as u64).saturating_mul(c as u64))
+			.saturating_add(RocksDbWeight::get().reads(1 as u64))
+			.saturating_add(RocksDbWeight::get().writes(2 as u64))
+	}
+	fn release_license(c: u32) -> Weight {
+		Weight::from_ref_time(55_336_000 as u64)
+			// Standard Error: 0
+			.saturating_add(Weight::from_ref_time(151_000 as u64).saturating_mul(c as u64))
+			.saturating_add(RocksDbWeight::get().reads(1 as u64))
+			.saturating_add(RocksDbWeight::get().writes(2 as u64))
+	}
+	fn force_release_license(c: u32) -> Weight {
+		Weight::from_ref_time(55_336_000 as u64)
+			// Standard Error: 0
+			.saturating_add(Weight::from_ref_time(151_000 as u64).saturating_mul(c as u64))
+			.saturating_add(RocksDbWeight::get().reads(1 as u64))
+			.saturating_add(RocksDbWeight::get().writes(2 as u64))
+	}
+	fn note_author() -> Weight {
+		Weight::from_ref_time(71_461_000 as u64)
+			.saturating_add(RocksDbWeight::get().reads(3 as u64))
+			.saturating_add(RocksDbWeight::get().writes(4 as u64))
+	}
+	fn new_session(r: u32, c: u32) -> Weight {
+		Weight::from_ref_time(0 as u64)
+			// Standard Error: 1_010_000
+			.saturating_add(Weight::from_ref_time(109_961_000 as u64).saturating_mul(r as u64))
+			// Standard Error: 1_010_000
+			.saturating_add(Weight::from_ref_time(151_952_000 as u64).saturating_mul(c as u64))
+			.saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(r as u64)))
+			.saturating_add(RocksDbWeight::get().reads((2 as u64).saturating_mul(c as u64)))
+			.saturating_add(RocksDbWeight::get().writes((2 as u64).saturating_mul(r as u64)))
+			.saturating_add(RocksDbWeight::get().writes((2 as u64).saturating_mul(c as u64)))
+	}
+}
modifiedpallets/configuration/CHANGELOG.mddiffbeforeafterboth
--- a/pallets/configuration/CHANGELOG.md
+++ b/pallets/configuration/CHANGELOG.md
@@ -1,4 +1,15 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+
 <!-- bureaucrate goes here -->
+
+## [0.1.3] - 2022-12-29
+
+### Added
+
+- The ability to configure pallet `collator-selection` via the `configuration` pallet.
+
 ## [0.1.2] - 2022-12-13
 
 ### Added
modifiedpallets/configuration/Cargo.tomldiffbeforeafterboth
--- a/pallets/configuration/Cargo.toml
+++ b/pallets/configuration/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "pallet-configuration"
-version = "0.1.2"
+version = "0.1.3"
 edition = "2021"
 
 [dependencies]
@@ -12,6 +12,7 @@
 ] }
 frame-support = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
 frame-system = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
 sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
 sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
 sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
@@ -22,10 +23,12 @@
 
 [features]
 default = ["std"]
+runtime-benchmarks = ["frame-benchmarking"]
 std = [
 	"parity-scale-codec/std",
 	"frame-support/std",
 	"frame-system/std",
+	"frame-benchmarking/std",
 	"sp-runtime/std",
 	"sp-std/std",
 	"sp-core/std",
addedpallets/configuration/src/benchmarking.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/configuration/src/benchmarking.rs
@@ -0,0 +1,100 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+//! Benchmarking setup for pallet-configuration
+
+use super::*;
+use frame_benchmarking::benchmarks;
+use frame_system::{EventRecord, RawOrigin};
+use frame_support::{assert_ok, BoundedVec, traits::Currency};
+use xcm::v1::MultiLocation;
+
+fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
+	let events = frame_system::Pallet::<T>::events();
+	let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into();
+	// compare to the last event record
+	let EventRecord { event, .. } = &events[events.len() - 1];
+	assert_eq!(event, &system_event);
+}
+
+benchmarks! {
+	where_clause { where T: Config }
+
+	set_weight_to_fee_coefficient_override {
+		let coeff: u64 = 999;
+	}: {
+		assert_ok!(
+			<Pallet<T>>::set_weight_to_fee_coefficient_override(RawOrigin::Root.into(), Some(coeff))
+		);
+	}
+
+	set_min_gas_price_override {
+		let coeff: u64 = 999;
+	}: {
+		assert_ok!(
+			<Pallet<T>>::set_min_gas_price_override(RawOrigin::Root.into(), Some(coeff))
+		);
+	}
+
+	set_xcm_allowed_locations {
+		let locations: BoundedVec<MultiLocation, T::MaxXcmAllowedLocations> = Default::default();
+	}: {
+		assert_ok!(
+			<Pallet<T>>::set_xcm_allowed_locations(RawOrigin::Root.into(), Some(locations))
+		);
+	}
+
+	set_app_promotion_configuration_override {
+		let configuration: AppPromotionConfiguration<T::BlockNumber> = Default::default();
+	}: {
+		assert_ok!(
+			<Pallet<T>>::set_app_promotion_configuration_override(RawOrigin::Root.into(), configuration)
+		);
+	}
+
+	set_collator_selection_desired_collators {
+		let max: u32 = 999;
+	}: {
+		assert_ok!(
+			<Pallet<T>>::set_collator_selection_desired_collators(RawOrigin::Root.into(), Some(max.clone()))
+		);
+	}
+	verify {
+		assert_last_event::<T>(Event::NewDesiredCollators{desired_collators: Some(max)}.into());
+	}
+
+	set_collator_selection_license_bond {
+		let bond_cost: Option<BalanceOf<T>> = Some(T::Currency::minimum_balance() * 10u32.into());
+	}: {
+		assert_ok!(
+			<Pallet<T>>::set_collator_selection_license_bond(RawOrigin::Root.into(), bond_cost.clone())
+		);
+	}
+	verify {
+		assert_last_event::<T>(Event::NewCollatorLicenseBond{bond_cost}.into());
+	}
+
+	set_collator_selection_kick_threshold {
+		let threshold: Option<T::BlockNumber> = Some(900u32.into());
+	}: {
+		assert_ok!(
+			<Pallet<T>>::set_collator_selection_kick_threshold(RawOrigin::Root.into(), threshold.clone())
+		);
+	}
+	verify {
+		assert_last_event::<T>(Event::NewCollatorKickThreshold{length_in_blocks: threshold}.into());
+	}
+}
modifiedpallets/configuration/src/lib.rsdiffbeforeafterboth
--- a/pallets/configuration/src/lib.rs
+++ b/pallets/configuration/src/lib.rs
@@ -34,19 +34,33 @@
 pub use pallet::*;
 use sp_core::U256;
 
+#[cfg(feature = "runtime-benchmarks")]
+mod benchmarking;
+pub mod weights;
+
 #[pallet]
 mod pallet {
 	use super::*;
 	use frame_support::{
-		traits::Get,
-		pallet_prelude::{StorageValue, ValueQuery, DispatchResult, OptionQuery},
-		BoundedVec,
+		traits::{Get, ReservableCurrency, Currency},
+		pallet_prelude::{StorageValue, ValueQuery, DispatchResult, IsType, OptionQuery},
+		BoundedVec, log,
 	};
-	use frame_system::{pallet_prelude::OriginFor, ensure_root};
+	use frame_system::{pallet_prelude::OriginFor, ensure_root, Config as SystemConfig};
 	use xcm::v1::MultiLocation;
 
+	pub use crate::weights::WeightInfo;
+	pub type BalanceOf<T> =
+		<<T as Config>::Currency as Currency<<T as SystemConfig>::AccountId>>::Balance;
+
 	#[pallet::config]
 	pub trait Config: frame_system::Config {
+		/// Overarching event type.
+		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
+
+		/// The currency mechanism.
+		type Currency: ReservableCurrency<Self::AccountId>;
+
 		#[pallet::constant]
 		type DefaultWeightToFeeCoefficient: Get<u64>;
 		#[pallet::constant]
@@ -58,6 +72,30 @@
 		type AppPromotionDailyRate: Get<Perbill>;
 		#[pallet::constant]
 		type DayRelayBlocks: Get<Self::BlockNumber>;
+
+		#[pallet::constant]
+		type DefaultCollatorSelectionMaxCollators: Get<u32>;
+		#[pallet::constant]
+		type DefaultCollatorSelectionLicenseBond: Get<BalanceOf<Self>>;
+		#[pallet::constant]
+		type DefaultCollatorSelectionKickThreshold: Get<Self::BlockNumber>;
+
+		/// The weight information of this pallet.
+		type WeightInfo: WeightInfo;
+	}
+
+	#[pallet::event]
+	#[pallet::generate_deposit(pub(super) fn deposit_event)]
+	pub enum Event<T: Config> {
+		NewDesiredCollators {
+			desired_collators: Option<u32>,
+		},
+		NewCollatorLicenseBond {
+			bond_cost: Option<BalanceOf<T>>,
+		},
+		NewCollatorKickThreshold {
+			length_in_blocks: Option<T::BlockNumber>,
+		},
 	}
 
 	#[pallet::error]
@@ -86,10 +124,31 @@
 	pub type AppPromomotionConfigurationOverride<T: Config> =
 		StorageValue<Value = AppPromotionConfiguration<T::BlockNumber>, QueryKind = ValueQuery>;
 
+	#[pallet::storage]
+	pub type CollatorSelectionDesiredCollatorsOverride<T: Config> = StorageValue<
+		Value = u32,
+		QueryKind = ValueQuery,
+		OnEmpty = T::DefaultCollatorSelectionMaxCollators,
+	>;
+
+	#[pallet::storage]
+	pub type CollatorSelectionLicenseBondOverride<T: Config> = StorageValue<
+		Value = BalanceOf<T>,
+		QueryKind = ValueQuery,
+		OnEmpty = T::DefaultCollatorSelectionLicenseBond,
+	>;
+
+	#[pallet::storage]
+	pub type CollatorSelectionKickThresholdOverride<T: Config> = StorageValue<
+		Value = T::BlockNumber,
+		QueryKind = ValueQuery,
+		OnEmpty = T::DefaultCollatorSelectionKickThreshold,
+	>;
+
 	#[pallet::call]
 	impl<T: Config> Pallet<T> {
 		#[pallet::call_index(0)]
-		#[pallet::weight(T::DbWeight::get().writes(1))]
+		#[pallet::weight(T::WeightInfo::set_weight_to_fee_coefficient_override())]
 		pub fn set_weight_to_fee_coefficient_override(
 			origin: OriginFor<T>,
 			coeff: Option<u64>,
@@ -104,7 +163,7 @@
 		}
 
 		#[pallet::call_index(1)]
-		#[pallet::weight(T::DbWeight::get().writes(1))]
+		#[pallet::weight(T::WeightInfo::set_min_gas_price_override())]
 		pub fn set_min_gas_price_override(
 			origin: OriginFor<T>,
 			coeff: Option<u64>,
@@ -119,7 +178,7 @@
 		}
 
 		#[pallet::call_index(2)]
-		#[pallet::weight(T::DbWeight::get().writes(1))]
+		#[pallet::weight(T::WeightInfo::set_xcm_allowed_locations())]
 		pub fn set_xcm_allowed_locations(
 			origin: OriginFor<T>,
 			locations: Option<BoundedVec<MultiLocation, T::MaxXcmAllowedLocations>>,
@@ -130,7 +189,7 @@
 		}
 
 		#[pallet::call_index(3)]
-		#[pallet::weight(T::DbWeight::get().writes(1))]
+		#[pallet::weight(T::WeightInfo::set_app_promotion_configuration_override())]
 		pub fn set_app_promotion_configuration_override(
 			origin: OriginFor<T>,
 			mut configuration: AppPromotionConfiguration<T::BlockNumber>,
@@ -149,6 +208,62 @@
 
 			Ok(())
 		}
+
+		#[pallet::call_index(4)]
+		#[pallet::weight(T::WeightInfo::set_collator_selection_desired_collators())]
+		pub fn set_collator_selection_desired_collators(
+			origin: OriginFor<T>,
+			max: Option<u32>,
+		) -> DispatchResult {
+			ensure_root(origin)?;
+			if let Some(max) = max {
+				// we trust origin calls, this is just a for more accurate benchmarking
+				if max > T::DefaultCollatorSelectionMaxCollators::get() {
+					log::warn!("max > T::DefaultCollatorSelectionMaxCollators; you might need to run benchmarks again");
+				}
+				<CollatorSelectionDesiredCollatorsOverride<T>>::set(max);
+			} else {
+				<CollatorSelectionDesiredCollatorsOverride<T>>::kill();
+			}
+			Self::deposit_event(Event::NewDesiredCollators {
+				desired_collators: max,
+			});
+			Ok(())
+		}
+
+		#[pallet::call_index(5)]
+		#[pallet::weight(T::WeightInfo::set_collator_selection_license_bond())]
+		pub fn set_collator_selection_license_bond(
+			origin: OriginFor<T>,
+			amount: Option<BalanceOf<T>>,
+		) -> DispatchResult {
+			ensure_root(origin)?;
+			if let Some(amount) = amount {
+				<CollatorSelectionLicenseBondOverride<T>>::set(amount);
+			} else {
+				<CollatorSelectionLicenseBondOverride<T>>::kill();
+			}
+			Self::deposit_event(Event::NewCollatorLicenseBond { bond_cost: amount });
+			Ok(())
+		}
+
+		#[pallet::call_index(6)]
+		#[pallet::weight(T::WeightInfo::set_collator_selection_kick_threshold())]
+		pub fn set_collator_selection_kick_threshold(
+			origin: OriginFor<T>,
+			threshold: Option<T::BlockNumber>,
+		) -> DispatchResult {
+			ensure_root(origin)?;
+			if let Some(threshold) = threshold {
+				<CollatorSelectionKickThresholdOverride<T>>::set(threshold);
+			} else {
+				<CollatorSelectionKickThresholdOverride<T>>::kill();
+			}
+			Self::deposit_event(Event::NewCollatorKickThreshold {
+				length_in_blocks: threshold,
+			});
+			Ok(())
+		}
 	}
 
 	#[pallet::pallet]
addedpallets/configuration/src/weights.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/configuration/src/weights.rs
@@ -0,0 +1,123 @@
+// Template adopted from https://github.com/paritytech/substrate/blob/master/.maintain/frame-weight-template.hbs
+
+//! Autogenerated weights for pallet_configuration
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
+//! DATE: 2022-12-28, STEPS: `50`, REPEAT: 80, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024
+
+// Executed Command:
+// target/release/unique-collator
+// benchmark
+// pallet
+// --pallet
+// pallet-configuration
+// --wasm-execution
+// compiled
+// --extrinsic
+// *
+// --template
+// .maintain/frame-weight-template.hbs
+// --steps=50
+// --repeat=80
+// --heap-pages=4096
+// --output=./pallets/configuration/src/weights.rs
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+#![allow(missing_docs)]
+#![allow(clippy::unnecessary_cast)]
+
+use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
+use sp_std::marker::PhantomData;
+
+/// Weight functions needed for pallet_configuration.
+pub trait WeightInfo {
+	fn set_weight_to_fee_coefficient_override() -> Weight;
+	fn set_min_gas_price_override() -> Weight;
+	fn set_xcm_allowed_locations() -> Weight;
+	fn set_app_promotion_configuration_override() -> Weight;
+	fn set_collator_selection_desired_collators() -> Weight;
+	fn set_collator_selection_license_bond() -> Weight;
+	fn set_collator_selection_kick_threshold() -> Weight;
+}
+
+/// Weights for pallet_configuration using the Substrate node and recommended hardware.
+pub struct SubstrateWeight<T>(PhantomData<T>);
+impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
+	// Storage: Configuration WeightToFeeCoefficientOverride (r:0 w:1)
+	fn set_weight_to_fee_coefficient_override() -> Weight {
+		Weight::from_ref_time(5_691_000 as u64)
+			.saturating_add(T::DbWeight::get().writes(1 as u64))
+	}
+	// Storage: Configuration MinGasPriceOverride (r:0 w:1)
+	fn set_min_gas_price_override() -> Weight {
+		Weight::from_ref_time(5_521_000 as u64)
+			.saturating_add(T::DbWeight::get().writes(1 as u64))
+	}
+	// Storage: Configuration XcmAllowedLocationsOverride (r:0 w:1)
+	fn set_xcm_allowed_locations() -> Weight {
+		Weight::from_ref_time(6_091_000 as u64)
+			.saturating_add(T::DbWeight::get().writes(1 as u64))
+	}
+	// Storage: Configuration AppPromomotionConfigurationOverride (r:0 w:1)
+	fn set_app_promotion_configuration_override() -> Weight {
+		Weight::from_ref_time(6_241_000 as u64)
+			.saturating_add(T::DbWeight::get().writes(1 as u64))
+	}
+	// Storage: Configuration CollatorSelectionDesiredCollatorsOverride (r:0 w:1)
+	fn set_collator_selection_desired_collators() -> Weight {
+		Weight::from_ref_time(25_298_000 as u64)
+			.saturating_add(T::DbWeight::get().writes(1 as u64))
+	}
+	// Storage: Configuration CollatorSelectionLicenseBondOverride (r:0 w:1)
+	fn set_collator_selection_license_bond() -> Weight {
+		Weight::from_ref_time(18_675_000 as u64)
+			.saturating_add(T::DbWeight::get().writes(1 as u64))
+	}
+	// Storage: Configuration CollatorSelectionKickThresholdOverride (r:0 w:1)
+	fn set_collator_selection_kick_threshold() -> Weight {
+		Weight::from_ref_time(18_044_000 as u64)
+			.saturating_add(T::DbWeight::get().writes(1 as u64))
+	}
+}
+
+// For backwards compatibility and tests
+impl WeightInfo for () {
+	// Storage: Configuration WeightToFeeCoefficientOverride (r:0 w:1)
+	fn set_weight_to_fee_coefficient_override() -> Weight {
+		Weight::from_ref_time(5_691_000 as u64)
+			.saturating_add(RocksDbWeight::get().writes(1 as u64))
+	}
+	// Storage: Configuration MinGasPriceOverride (r:0 w:1)
+	fn set_min_gas_price_override() -> Weight {
+		Weight::from_ref_time(5_521_000 as u64)
+			.saturating_add(RocksDbWeight::get().writes(1 as u64))
+	}
+	// Storage: Configuration XcmAllowedLocationsOverride (r:0 w:1)
+	fn set_xcm_allowed_locations() -> Weight {
+		Weight::from_ref_time(6_091_000 as u64)
+			.saturating_add(RocksDbWeight::get().writes(1 as u64))
+	}
+	// Storage: Configuration AppPromomotionConfigurationOverride (r:0 w:1)
+	fn set_app_promotion_configuration_override() -> Weight {
+		Weight::from_ref_time(6_241_000 as u64)
+			.saturating_add(RocksDbWeight::get().writes(1 as u64))
+	}
+	// Storage: Configuration CollatorSelectionDesiredCollatorsOverride (r:0 w:1)
+	fn set_collator_selection_desired_collators() -> Weight {
+		Weight::from_ref_time(25_298_000 as u64)
+			.saturating_add(RocksDbWeight::get().writes(1 as u64))
+	}
+	// Storage: Configuration CollatorSelectionLicenseBondOverride (r:0 w:1)
+	fn set_collator_selection_license_bond() -> Weight {
+		Weight::from_ref_time(18_675_000 as u64)
+			.saturating_add(RocksDbWeight::get().writes(1 as u64))
+	}
+	// Storage: Configuration CollatorSelectionKickThresholdOverride (r:0 w:1)
+	fn set_collator_selection_kick_threshold() -> Weight {
+		Weight::from_ref_time(18_044_000 as u64)
+			.saturating_add(RocksDbWeight::get().writes(1 as u64))
+	}
+}
addedpallets/identity/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/pallets/identity/Cargo.toml
@@ -0,0 +1,47 @@
+[package]
+name = "pallet-identity"
+version = "4.0.0-dev"
+authors = ["Parity Technologies <admin@parity.io>", "Unique Network <support@uniquenetwork.io>"]
+edition = "2021"
+license = "Apache-2.0"
+homepage = "https://substrate.io"
+repository = "https://github.com/paritytech/substrate/"
+description = "FRAME identity management pallet"
+readme = "README.md"
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
+
+[dependencies]
+codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] }
+enumflags2 = { version = "0.7.4" }
+scale-info = { version = "2.1.1", default-features = false, features = ["derive"] }
+frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+sp-io = { version = "7.0.0", default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+sp-runtime = { version = "7.0.0", default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+sp-std = { version = "5.0.0", default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+
+[dev-dependencies]
+pallet-balances = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+sp-core = { version = "7.0.0", git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.36" }
+
+[features]
+default = ["std"]
+std = [
+	"codec/std",
+	"frame-benchmarking?/std",
+	"frame-support/std",
+	"frame-system/std",
+	"scale-info/std",
+	"sp-io/std",
+	"sp-runtime/std",
+	"sp-std/std",
+]
+runtime-benchmarks = [
+	"frame-benchmarking/runtime-benchmarks",
+	"frame-support/runtime-benchmarks",
+	"frame-system/runtime-benchmarks",
+]
+try-runtime = ["frame-support/try-runtime"]
addedpallets/identity/README.mddiffbeforeafterboth
--- /dev/null
+++ b/pallets/identity/README.md
@@ -0,0 +1,56 @@
+# Identity Module
+
+- [`identity::Config`](https://docs.rs/pallet-identity/latest/pallet_identity/trait.Config.html)
+- [`Call`](https://docs.rs/pallet-identity/latest/pallet_identity/enum.Call.html)
+
+## Overview
+
+A federated naming system, allowing for multiple registrars to be added from a specified origin.
+Registrars can set a fee to provide identity-verification service. Anyone can put forth a
+proposed identity for a fixed deposit and ask for review by any number of registrars (paying
+each of their fees). Registrar judgements are given as an `enum`, allowing for sophisticated,
+multi-tier opinions.
+
+Some judgements are identified as *sticky*, which means they cannot be removed except by
+complete removal of the identity, or by the registrar. Judgements are allowed to represent a
+portion of funds that have been reserved for the registrar.
+
+A super-user can remove accounts and in doing so, slash the deposit.
+
+All accounts may also have a limited number of sub-accounts which may be specified by the owner;
+by definition, these have equivalent ownership and each has an individual name.
+
+The number of registrars should be limited, and the deposit made sufficiently large, to ensure
+no state-bloat attack is viable.
+
+## Interface
+
+### Dispatchable Functions
+
+#### For general users
+* `set_identity` - Set the associated identity of an account; a small deposit is reserved if not
+  already taken.
+* `clear_identity` - Remove an account's associated identity; the deposit is returned.
+* `request_judgement` - Request a judgement from a registrar, paying a fee.
+* `cancel_request` - Cancel the previous request for a judgement.
+
+#### For general users with sub-identities
+* `set_subs` - Set the sub-accounts of an identity.
+* `add_sub` - Add a sub-identity to an identity.
+* `remove_sub` - Remove a sub-identity of an identity.
+* `rename_sub` - Rename a sub-identity of an identity.
+* `quit_sub` - Remove a sub-identity of an identity (called by the sub-identity).
+
+#### For registrars
+* `set_fee` - Set the fee required to be paid for a judgement to be given by the registrar.
+* `set_fields` - Set the fields that a registrar cares about in their judgements.
+* `provide_judgement` - Provide a judgement to an identity.
+
+#### For super-users
+* `add_registrar` - Add a new registrar to the system.
+* `kill_identity` - Forcibly remove the associated identity; the deposit is lost.
+
+[`Call`]: ./enum.Call.html
+[`Config`]: ./trait.Config.html
+
+License: Apache-2.0
addedpallets/identity/src/benchmarking.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/identity/src/benchmarking.rs
@@ -0,0 +1,500 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+// Original license:
+// This file is part of Substrate.
+
+// Copyright (C) 2020-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.
+
+//! Identity pallet benchmarking.
+
+#![cfg(feature = "runtime-benchmarks")]
+
+use super::*;
+
+use crate::Pallet as Identity;
+use frame_benchmarking::{account, benchmarks, whitelisted_caller};
+use frame_support::{
+	ensure, assert_ok,
+	traits::{EnsureOrigin, Get},
+};
+use frame_system::RawOrigin;
+use sp_runtime::traits::Bounded;
+
+const SEED: u32 = 0;
+
+fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
+	frame_system::Pallet::<T>::assert_last_event(generic_event.into());
+}
+
+// Adds `r` registrars to the Identity Pallet. These registrars will have set fees and fields.
+fn add_registrars<T: Config>(r: u32) -> Result<(), &'static str> {
+	for i in 0..r {
+		let registrar: T::AccountId = account("registrar", i, SEED);
+		let registrar_lookup = T::Lookup::unlookup(registrar.clone());
+		let _ = T::Currency::make_free_balance_be(&registrar, BalanceOf::<T>::max_value());
+		let registrar_origin = T::RegistrarOrigin::successful_origin();
+		Identity::<T>::add_registrar(registrar_origin, registrar_lookup)?;
+		Identity::<T>::set_fee(RawOrigin::Signed(registrar.clone()).into(), i, 10u32.into())?;
+		let fields = IdentityFields(
+			IdentityField::Display
+				| IdentityField::Legal
+				| IdentityField::Web
+				| IdentityField::Riot
+				| IdentityField::Email
+				| IdentityField::PgpFingerprint
+				| IdentityField::Image
+				| IdentityField::Twitter,
+		);
+		Identity::<T>::set_fields(RawOrigin::Signed(registrar.clone()).into(), i, fields)?;
+	}
+
+	assert_eq!(Registrars::<T>::get().len(), r as usize);
+	Ok(())
+}
+
+// Create `s` sub-accounts for the identity of `who` and return them.
+// Each will have 32 bytes of raw data added to it.
+fn create_sub_accounts<T: Config>(
+	who: &T::AccountId,
+	s: u32,
+) -> Result<Vec<(T::AccountId, Data)>, &'static str> {
+	let mut subs = Vec::new();
+	let who_origin = RawOrigin::Signed(who.clone());
+	let data = Data::Raw(vec![0; 32].try_into().unwrap());
+
+	for i in 0..s {
+		let sub_account = account("sub", i, SEED);
+		subs.push((sub_account, data.clone()));
+	}
+
+	// Set identity so `set_subs` does not fail.
+	if IdentityOf::<T>::get(who).is_none() {
+		let _ = T::Currency::make_free_balance_be(who, BalanceOf::<T>::max_value() / 2u32.into());
+		let info = create_identity_info::<T>(1);
+		Identity::<T>::set_identity(who_origin.into(), Box::new(info))?;
+	}
+
+	Ok(subs)
+}
+
+// Adds `s` sub-accounts to the identity of `who`. Each will have 32 bytes of raw data added to it.
+// This additionally returns the vector of sub-accounts so it can be modified if needed.
+fn add_sub_accounts<T: Config>(
+	who: &T::AccountId,
+	s: u32,
+) -> Result<Vec<(T::AccountId, Data)>, &'static str> {
+	let who_origin = RawOrigin::Signed(who.clone());
+	let subs = create_sub_accounts::<T>(who, s)?;
+
+	Identity::<T>::set_subs(who_origin.into(), subs.clone())?;
+
+	Ok(subs)
+}
+
+// This creates an `IdentityInfo` object with `num_fields` extra fields.
+// All data is pre-populated with some arbitrary bytes.
+fn create_identity_info<T: Config>(num_fields: u32) -> IdentityInfo<T::MaxAdditionalFields> {
+	let data = Data::Raw(vec![0; 32].try_into().unwrap());
+
+	IdentityInfo {
+		additional: vec![(data.clone(), data.clone()); num_fields as usize]
+			.try_into()
+			.unwrap(),
+		display: data.clone(),
+		legal: data.clone(),
+		web: data.clone(),
+		riot: data.clone(),
+		email: data.clone(),
+		pgp_fingerprint: Some([0; 20]),
+		image: data.clone(),
+		twitter: data,
+	}
+}
+
+benchmarks! {
+	add_registrar {
+		let r in 1 .. T::MaxRegistrars::get() - 1 => add_registrars::<T>(r)?;
+		ensure!(Registrars::<T>::get().len() as u32 == r, "Registrars not set up correctly.");
+		let origin = T::RegistrarOrigin::successful_origin();
+		let account = T::Lookup::unlookup(account("registrar", r + 1, SEED));
+	}: _<T::RuntimeOrigin>(origin, account)
+	verify {
+		ensure!(Registrars::<T>::get().len() as u32 == r + 1, "Registrars not added.");
+	}
+
+	set_identity {
+		let r in 1 .. T::MaxRegistrars::get() => add_registrars::<T>(r)?;
+		let x in 0 .. T::MaxAdditionalFields::get();
+		let caller = {
+			// The target user
+			let caller: T::AccountId = whitelisted_caller();
+			let caller_lookup = T::Lookup::unlookup(caller.clone());
+			let caller_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into();
+			let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
+
+			// Add an initial identity
+			let initial_info = create_identity_info::<T>(1);
+			Identity::<T>::set_identity(caller_origin.clone(), Box::new(initial_info.clone()))?;
+
+			// User requests judgement from all the registrars, and they approve
+			for i in 0..r {
+				let registrar: T::AccountId = account("registrar", i, SEED);
+				let registrar_lookup = T::Lookup::unlookup(registrar.clone());
+				let balance_to_use =  T::Currency::minimum_balance() * 10u32.into();
+				let _ = T::Currency::make_free_balance_be(&registrar, balance_to_use);
+
+				Identity::<T>::request_judgement(caller_origin.clone(), i, 10u32.into())?;
+				Identity::<T>::provide_judgement(
+					RawOrigin::Signed(registrar).into(),
+					i,
+					caller_lookup.clone(),
+					Judgement::Reasonable,
+					T::Hashing::hash_of(&initial_info),
+				)?;
+			}
+			caller
+		};
+	}: _(RawOrigin::Signed(caller.clone()), Box::new(create_identity_info::<T>(x)))
+	verify {
+		assert_last_event::<T>(Event::<T>::IdentitySet { who: caller }.into());
+	}
+
+	// We need to split `set_subs` into two benchmarks to accurately isolate the potential
+	// writes caused by new or old sub accounts. The actual weight should simply be
+	// the sum of these two weights.
+	set_subs_new {
+		let caller: T::AccountId = whitelisted_caller();
+		// Create a new subs vec with s sub accounts
+		let s in 0 .. T::MaxSubAccounts::get() => ();
+		let subs = create_sub_accounts::<T>(&caller, s)?;
+		ensure!(SubsOf::<T>::get(&caller).1.len() == 0, "Caller already has subs");
+	}: set_subs(RawOrigin::Signed(caller.clone()), subs)
+	verify {
+		ensure!(SubsOf::<T>::get(&caller).1.len() as u32 == s, "Subs not added");
+	}
+
+	set_subs_old {
+		let caller: T::AccountId = whitelisted_caller();
+		// Give them p many previous sub accounts.
+		let p in 0 .. T::MaxSubAccounts::get() => {
+			let _ = add_sub_accounts::<T>(&caller, p)?;
+		};
+		// Remove all subs.
+		let subs = create_sub_accounts::<T>(&caller, 0)?;
+		ensure!(
+			SubsOf::<T>::get(&caller).1.len() as u32 == p,
+			"Caller does have subs",
+		);
+	}: set_subs(RawOrigin::Signed(caller.clone()), subs)
+	verify {
+		ensure!(SubsOf::<T>::get(&caller).1.len() == 0, "Subs not removed");
+	}
+
+	clear_identity {
+		let caller: T::AccountId = whitelisted_caller();
+		let caller_origin = <T as frame_system::Config>::RuntimeOrigin::from(RawOrigin::Signed(caller.clone()));
+		let caller_lookup = <T::Lookup as StaticLookup>::unlookup(caller.clone());
+		let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
+
+		let r in 1 .. T::MaxRegistrars::get() => add_registrars::<T>(r)?;
+		let s in 0 .. T::MaxSubAccounts::get() => {
+			// Give them s many sub accounts
+			let caller: T::AccountId = whitelisted_caller();
+			let _ = add_sub_accounts::<T>(&caller, s)?;
+		};
+		let x in 0 .. T::MaxAdditionalFields::get();
+
+		// Create their main identity with x additional fields
+		let info = create_identity_info::<T>(x);
+		let caller: T::AccountId = whitelisted_caller();
+		let caller_origin = <T as frame_system::Config>::RuntimeOrigin::from(RawOrigin::Signed(caller.clone()));
+		Identity::<T>::set_identity(caller_origin.clone(), Box::new(info.clone()))?;
+
+		// User requests judgement from all the registrars, and they approve
+		for i in 0..r {
+			let registrar: T::AccountId = account("registrar", i, SEED);
+			let balance_to_use =  T::Currency::minimum_balance() * 10u32.into();
+			let _ = T::Currency::make_free_balance_be(&registrar, balance_to_use);
+
+			Identity::<T>::request_judgement(caller_origin.clone(), i, 10u32.into())?;
+			Identity::<T>::provide_judgement(
+				RawOrigin::Signed(registrar).into(),
+				i,
+				caller_lookup.clone(),
+				Judgement::Reasonable,
+				T::Hashing::hash_of(&info),
+			)?;
+		}
+		ensure!(IdentityOf::<T>::contains_key(&caller), "Identity does not exist.");
+	}: _(RawOrigin::Signed(caller.clone()))
+	verify {
+		ensure!(!IdentityOf::<T>::contains_key(&caller), "Identity not cleared.");
+	}
+
+	request_judgement {
+		let caller: T::AccountId = whitelisted_caller();
+		let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
+
+		let r in 1 .. T::MaxRegistrars::get() => add_registrars::<T>(r)?;
+		let x in 0 .. T::MaxAdditionalFields::get() => {
+			// Create their main identity with x additional fields
+			let info = create_identity_info::<T>(x);
+			let caller: T::AccountId = whitelisted_caller();
+			let caller_origin = <T as frame_system::Config>::RuntimeOrigin::from(RawOrigin::Signed(caller));
+			Identity::<T>::set_identity(caller_origin, Box::new(info))?;
+		};
+	}: _(RawOrigin::Signed(caller.clone()), r - 1, 10u32.into())
+	verify {
+		assert_last_event::<T>(Event::<T>::JudgementRequested { who: caller, registrar_index: r-1 }.into());
+	}
+
+	cancel_request {
+		let caller: T::AccountId = whitelisted_caller();
+		let caller_origin = <T as frame_system::Config>::RuntimeOrigin::from(RawOrigin::Signed(caller.clone()));
+		let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
+
+		let r in 1 .. T::MaxRegistrars::get() => add_registrars::<T>(r)?;
+		let x in 0 .. T::MaxAdditionalFields::get() => {
+			// Create their main identity with x additional fields
+			let info = create_identity_info::<T>(x);
+			let caller: T::AccountId = whitelisted_caller();
+			let caller_origin = <T as frame_system::Config>::RuntimeOrigin::from(RawOrigin::Signed(caller));
+			Identity::<T>::set_identity(caller_origin, Box::new(info))?;
+		};
+
+		Identity::<T>::request_judgement(caller_origin, r - 1, 10u32.into())?;
+	}: _(RawOrigin::Signed(caller.clone()), r - 1)
+	verify {
+		assert_last_event::<T>(Event::<T>::JudgementUnrequested { who: caller, registrar_index: r-1 }.into());
+	}
+
+	set_fee {
+		let caller: T::AccountId = whitelisted_caller();
+		let caller_lookup = T::Lookup::unlookup(caller.clone());
+
+		let r in 1 .. T::MaxRegistrars::get() - 1 => add_registrars::<T>(r)?;
+
+		let registrar_origin = T::RegistrarOrigin::successful_origin();
+		Identity::<T>::add_registrar(registrar_origin, caller_lookup)?;
+		let registrars = Registrars::<T>::get();
+		ensure!(registrars[r as usize].as_ref().unwrap().fee == 0u32.into(), "Fee already set.");
+	}: _(RawOrigin::Signed(caller), r, 100u32.into())
+	verify {
+		let registrars = Registrars::<T>::get();
+		ensure!(registrars[r as usize].as_ref().unwrap().fee == 100u32.into(), "Fee not changed.");
+	}
+
+	set_account_id {
+		let caller: T::AccountId = whitelisted_caller();
+		let caller_lookup = T::Lookup::unlookup(caller.clone());
+		let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
+
+		let r in 1 .. T::MaxRegistrars::get() - 1 => add_registrars::<T>(r)?;
+
+		let registrar_origin = T::RegistrarOrigin::successful_origin();
+		Identity::<T>::add_registrar(registrar_origin, caller_lookup)?;
+		let registrars = Registrars::<T>::get();
+		ensure!(registrars[r as usize].as_ref().unwrap().account == caller, "id not set.");
+		let new_account = T::Lookup::unlookup(account("new", 0, SEED));
+	}: _(RawOrigin::Signed(caller), r, new_account)
+	verify {
+		let registrars = Registrars::<T>::get();
+		ensure!(registrars[r as usize].as_ref().unwrap().account == account("new", 0, SEED), "id not changed.");
+	}
+
+	set_fields {
+		let caller: T::AccountId = whitelisted_caller();
+		let caller_lookup = T::Lookup::unlookup(caller.clone());
+		let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
+
+		let r in 1 .. T::MaxRegistrars::get() - 1 => add_registrars::<T>(r)?;
+
+		let registrar_origin = T::RegistrarOrigin::successful_origin();
+		Identity::<T>::add_registrar(registrar_origin, caller_lookup)?;
+		let fields = IdentityFields(
+			IdentityField::Display | IdentityField::Legal | IdentityField::Web | IdentityField::Riot
+			| IdentityField::Email | IdentityField::PgpFingerprint | IdentityField::Image | IdentityField::Twitter
+		);
+		let registrars = Registrars::<T>::get();
+		ensure!(registrars[r as usize].as_ref().unwrap().fields == Default::default(), "fields already set.");
+	}: _(RawOrigin::Signed(caller), r, fields)
+	verify {
+		let registrars = Registrars::<T>::get();
+		ensure!(registrars[r as usize].as_ref().unwrap().fields != Default::default(), "fields not set.");
+	}
+
+	provide_judgement {
+		// The user
+		let user: T::AccountId = account("user", r, SEED);
+		let user_origin = <T as frame_system::Config>::RuntimeOrigin::from(RawOrigin::Signed(user.clone()));
+		let user_lookup = <T::Lookup as StaticLookup>::unlookup(user.clone());
+		let _ = T::Currency::make_free_balance_be(&user, BalanceOf::<T>::max_value());
+
+		let caller: T::AccountId = whitelisted_caller();
+		let caller_lookup = T::Lookup::unlookup(caller.clone());
+		let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
+
+		let r in 1 .. T::MaxRegistrars::get() - 1 => add_registrars::<T>(r)?;
+		let x in 0 .. T::MaxAdditionalFields::get();
+
+		let info = create_identity_info::<T>(x);
+		let info_hash = T::Hashing::hash_of(&info);
+		Identity::<T>::set_identity(user_origin.clone(), Box::new(info))?;
+
+		let registrar_origin = T::RegistrarOrigin::successful_origin();
+		Identity::<T>::add_registrar(registrar_origin, caller_lookup)?;
+		Identity::<T>::request_judgement(user_origin, r, 10u32.into())?;
+	}: _(RawOrigin::Signed(caller), r, user_lookup, Judgement::Reasonable, info_hash)
+	verify {
+		assert_last_event::<T>(Event::<T>::JudgementGiven { target: user, registrar_index: r }.into())
+	}
+
+	kill_identity {
+		let r in 1 .. T::MaxRegistrars::get() => add_registrars::<T>(r)?;
+		let s in 0 .. T::MaxSubAccounts::get();
+		let x in 0 .. T::MaxAdditionalFields::get();
+
+		let target: T::AccountId = account("target", 0, SEED);
+		let target_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(target.clone()).into();
+		let target_lookup = T::Lookup::unlookup(target.clone());
+		let _ = T::Currency::make_free_balance_be(&target, BalanceOf::<T>::max_value());
+
+		let info = create_identity_info::<T>(x);
+		Identity::<T>::set_identity(target_origin.clone(), Box::new(info.clone()))?;
+		let _ = add_sub_accounts::<T>(&target, s)?;
+
+		// User requests judgement from all the registrars, and they approve
+		for i in 0..r {
+			let registrar: T::AccountId = account("registrar", i, SEED);
+			let balance_to_use =  T::Currency::minimum_balance() * 10u32.into();
+			let _ = T::Currency::make_free_balance_be(&registrar, balance_to_use);
+
+			Identity::<T>::request_judgement(target_origin.clone(), i, 10u32.into())?;
+			Identity::<T>::provide_judgement(
+				RawOrigin::Signed(registrar).into(),
+				i,
+				target_lookup.clone(),
+				Judgement::Reasonable,
+				T::Hashing::hash_of(&info),
+			)?;
+		}
+		ensure!(IdentityOf::<T>::contains_key(&target), "Identity not set");
+		let origin = T::ForceOrigin::successful_origin();
+	}: _<T::RuntimeOrigin>(origin, target_lookup)
+	verify {
+		ensure!(!IdentityOf::<T>::contains_key(&target), "Identity not removed");
+	}
+
+	force_insert_identities {
+		let x in 0 .. T::MaxAdditionalFields::get();
+		let n in 0..600;
+		use frame_benchmarking::account;
+		let identities = (0..n).map(|i| (
+			account("caller", i, 0),
+			Registration::<BalanceOf<T>, T::MaxRegistrars, T::MaxAdditionalFields> {
+				judgements: Default::default(),
+				deposit: Default::default(),
+				info: create_identity_info::<T>(x),
+			},
+		)).collect::<Vec<_>>();
+		let origin = T::ForceOrigin::successful_origin();
+	}: _<T::RuntimeOrigin>(origin, identities)
+
+	force_remove_identities {
+		let x in 0 .. T::MaxAdditionalFields::get();
+		let n in 0..600;
+		use frame_benchmarking::account;
+		let origin = T::ForceOrigin::successful_origin();
+		let identities = (0..n).map(|i| (
+			account("caller", i, 0),
+			Registration::<BalanceOf<T>, T::MaxRegistrars, T::MaxAdditionalFields> {
+				judgements: Default::default(),
+				deposit: Default::default(),
+				info: create_identity_info::<T>(x),
+			},
+		)).collect::<Vec<_>>();
+		assert_ok!(
+			Identity::<T>::force_insert_identities(origin.clone(), identities.clone()),
+		);
+		let identities = identities.into_iter().map(|(acc, _)| acc).collect::<Vec<_>>();
+	}: _<T::RuntimeOrigin>(origin, identities)
+
+	add_sub {
+		let s in 0 .. T::MaxSubAccounts::get() - 1;
+
+		let caller: T::AccountId = whitelisted_caller();
+		let _ = add_sub_accounts::<T>(&caller, s)?;
+		let sub = account("new_sub", 0, SEED);
+		let data = Data::Raw(vec![0; 32].try_into().unwrap());
+		ensure!(SubsOf::<T>::get(&caller).1.len() as u32 == s, "Subs not set.");
+	}: _(RawOrigin::Signed(caller.clone()), T::Lookup::unlookup(sub), data)
+	verify {
+		ensure!(SubsOf::<T>::get(&caller).1.len() as u32 == s + 1, "Subs not added.");
+	}
+
+	rename_sub {
+		let s in 1 .. T::MaxSubAccounts::get();
+
+		let caller: T::AccountId = whitelisted_caller();
+		let (sub, _) = add_sub_accounts::<T>(&caller, s)?.remove(0);
+		let data = Data::Raw(vec![1; 32].try_into().unwrap());
+		ensure!(SuperOf::<T>::get(&sub).unwrap().1 != data, "data already set");
+	}: _(RawOrigin::Signed(caller), T::Lookup::unlookup(sub.clone()), data.clone())
+	verify {
+		ensure!(SuperOf::<T>::get(&sub).unwrap().1 == data, "data not set");
+	}
+
+	remove_sub {
+		let s in 1 .. T::MaxSubAccounts::get();
+
+		let caller: T::AccountId = whitelisted_caller();
+		let (sub, _) = add_sub_accounts::<T>(&caller, s)?.remove(0);
+		ensure!(SuperOf::<T>::contains_key(&sub), "Sub doesn't exists");
+	}: _(RawOrigin::Signed(caller), T::Lookup::unlookup(sub.clone()))
+	verify {
+		ensure!(!SuperOf::<T>::contains_key(&sub), "Sub not removed");
+	}
+
+	quit_sub {
+		let s in 0 .. T::MaxSubAccounts::get() - 1;
+
+		let caller: T::AccountId = whitelisted_caller();
+		let sup = account("super", 0, SEED);
+		let _ = add_sub_accounts::<T>(&sup, s)?;
+		let sup_origin = RawOrigin::Signed(sup).into();
+		Identity::<T>::add_sub(sup_origin, T::Lookup::unlookup(caller.clone()), Data::Raw(vec![0; 32].try_into().unwrap()))?;
+		ensure!(SuperOf::<T>::contains_key(&caller), "Sub doesn't exists");
+	}: _(RawOrigin::Signed(caller.clone()))
+	verify {
+		ensure!(!SuperOf::<T>::contains_key(&caller), "Sub not removed");
+	}
+
+	impl_benchmark_test_suite!(Identity, crate::tests::new_test_ext(), crate::tests::Test);
+}
addedpallets/identity/src/lib.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/identity/src/lib.rs
@@ -0,0 +1,1166 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+// Original license:
+// 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.
+
+//! # Identity Pallet
+//!
+//! - [`Config`]
+//! - [`Call`]
+//!
+//! ## Overview
+//!
+//! A federated naming system, allowing for multiple registrars to be added from a specified origin.
+//! Registrars can set a fee to provide identity-verification service. Anyone can put forth a
+//! proposed identity for a fixed deposit and ask for review by any number of registrars (paying
+//! each of their fees). Registrar judgements are given as an `enum`, allowing for sophisticated,
+//! multi-tier opinions.
+//!
+//! Some judgements are identified as *sticky*, which means they cannot be removed except by
+//! complete removal of the identity, or by the registrar. Judgements are allowed to represent a
+//! portion of funds that have been reserved for the registrar.
+//!
+//! A super-user can remove accounts and in doing so, slash the deposit.
+//!
+//! All accounts may also have a limited number of sub-accounts which may be specified by the owner;
+//! by definition, these have equivalent ownership and each has an individual name.
+//!
+//! The number of registrars should be limited, and the deposit made sufficiently large, to ensure
+//! no state-bloat attack is viable.
+//!
+//! ## Interface
+//!
+//! ### Dispatchable Functions
+//!
+//! #### For general users
+//! * `set_identity` - Set the associated identity of an account; a small deposit is reserved if not
+//!   already taken.
+//! * `clear_identity` - Remove an account's associated identity; the deposit is returned.
+//! * `request_judgement` - Request a judgement from a registrar, paying a fee.
+//! * `cancel_request` - Cancel the previous request for a judgement.
+//!
+//! #### For general users with sub-identities
+//! * `set_subs` - Set the sub-accounts of an identity.
+//! * `add_sub` - Add a sub-identity to an identity.
+//! * `remove_sub` - Remove a sub-identity of an identity.
+//! * `rename_sub` - Rename a sub-identity of an identity.
+//! * `quit_sub` - Remove a sub-identity of an identity (called by the sub-identity).
+//!
+//! #### For registrars
+//! * `set_fee` - Set the fee required to be paid for a judgement to be given by the registrar.
+//! * `set_fields` - Set the fields that a registrar cares about in their judgements.
+//! * `provide_judgement` - Provide a judgement to an identity.
+//!
+//! #### For super-users
+//! * `add_registrar` - Add a new registrar to the system.
+//! * `kill_identity` - Forcibly remove the associated identity; the deposit is lost.
+//!
+//! [`Call`]: ./enum.Call.html
+//! [`Config`]: ./trait.Config.html
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+mod benchmarking;
+#[cfg(test)]
+mod tests;
+mod types;
+pub mod weights;
+
+use frame_support::traits::{BalanceStatus, Currency, OnUnbalanced, ReservableCurrency};
+use sp_runtime::traits::{AppendZerosInput, Hash, Saturating, StaticLookup, Zero};
+use sp_std::prelude::*;
+pub use weights::WeightInfo;
+
+pub use pallet::*;
+pub use types::{
+	Data, IdentityField, IdentityFields, IdentityInfo, Judgement, RegistrarIndex, RegistrarInfo,
+	Registration,
+};
+
+pub type BalanceOf<T> =
+	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
+type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency<
+	<T as frame_system::Config>::AccountId,
+>>::NegativeImbalance;
+type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
+
+#[frame_support::pallet]
+pub mod pallet {
+	use super::*;
+	use frame_support::pallet_prelude::*;
+	use frame_system::pallet_prelude::*;
+
+	#[pallet::config]
+	pub trait Config: frame_system::Config {
+		/// The overarching event type.
+		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
+
+		/// The currency trait.
+		type Currency: ReservableCurrency<Self::AccountId>;
+
+		/// The amount held on deposit for a registered identity
+		#[pallet::constant]
+		type BasicDeposit: Get<BalanceOf<Self>>;
+
+		/// The amount held on deposit per additional field for a registered identity.
+		#[pallet::constant]
+		type FieldDeposit: Get<BalanceOf<Self>>;
+
+		/// The amount held on deposit for a registered subaccount. This should account for the fact
+		/// that one storage item's value will increase by the size of an account ID, and there will
+		/// be another trie item whose value is the size of an account ID plus 32 bytes.
+		#[pallet::constant]
+		type SubAccountDeposit: Get<BalanceOf<Self>>;
+
+		/// The maximum number of sub-accounts allowed per identified account.
+		#[pallet::constant]
+		type MaxSubAccounts: Get<u32>;
+
+		/// Maximum number of additional fields that may be stored in an ID. Needed to bound the I/O
+		/// required to access an identity, but can be pretty high.
+		#[pallet::constant]
+		type MaxAdditionalFields: Get<u32>;
+
+		/// Maxmimum number of registrars allowed in the system. Needed to bound the complexity
+		/// of, e.g., updating judgements.
+		#[pallet::constant]
+		type MaxRegistrars: Get<u32>;
+
+		/// What to do with slashed funds.
+		type Slashed: OnUnbalanced<NegativeImbalanceOf<Self>>;
+
+		/// The origin which may forcibly set or remove a name. Root can always do this.
+		type ForceOrigin: EnsureOrigin<Self::RuntimeOrigin>;
+
+		/// The origin which may add or remove registrars. Root can always do this.
+		type RegistrarOrigin: EnsureOrigin<Self::RuntimeOrigin>;
+
+		/// Weight information for extrinsics in this pallet.
+		type WeightInfo: WeightInfo;
+	}
+
+	#[pallet::pallet]
+	#[pallet::generate_store(pub(super) trait Store)]
+	pub struct Pallet<T>(_);
+
+	/// Information that is pertinent to identify the entity behind an account.
+	///
+	/// TWOX-NOTE: OK ― `AccountId` is a secure hash.
+	#[pallet::storage]
+	#[pallet::getter(fn identity)]
+	pub(super) type IdentityOf<T: Config> = StorageMap<
+		_,
+		Twox64Concat,
+		T::AccountId,
+		Registration<BalanceOf<T>, T::MaxRegistrars, T::MaxAdditionalFields>,
+		OptionQuery,
+	>;
+
+	/// The super-identity of an alternative "sub" identity together with its name, within that
+	/// context. If the account is not some other account's sub-identity, then just `None`.
+	#[pallet::storage]
+	#[pallet::getter(fn super_of)]
+	pub(super) type SuperOf<T: Config> =
+		StorageMap<_, Blake2_128Concat, T::AccountId, (T::AccountId, Data), OptionQuery>;
+
+	/// Alternative "sub" identities of this account.
+	///
+	/// The first item is the deposit, the second is a vector of the accounts.
+	///
+	/// TWOX-NOTE: OK ― `AccountId` is a secure hash.
+	#[pallet::storage]
+	#[pallet::getter(fn subs_of)]
+	pub(super) type SubsOf<T: Config> = StorageMap<
+		_,
+		Twox64Concat,
+		T::AccountId,
+		(BalanceOf<T>, BoundedVec<T::AccountId, T::MaxSubAccounts>),
+		ValueQuery,
+	>;
+
+	/// The set of registrars. Not expected to get very big as can only be added through a
+	/// special origin (likely a council motion).
+	///
+	/// The index into this can be cast to `RegistrarIndex` to get a valid value.
+	#[pallet::storage]
+	#[pallet::getter(fn registrars)]
+	pub(super) type Registrars<T: Config> = StorageValue<
+		_,
+		BoundedVec<Option<RegistrarInfo<BalanceOf<T>, T::AccountId>>, T::MaxRegistrars>,
+		ValueQuery,
+	>;
+
+	#[pallet::error]
+	pub enum Error<T> {
+		/// Too many subs-accounts.
+		TooManySubAccounts,
+		/// Account isn't found.
+		NotFound,
+		/// Account isn't named.
+		NotNamed,
+		/// Empty index.
+		EmptyIndex,
+		/// Fee is changed.
+		FeeChanged,
+		/// No identity found.
+		NoIdentity,
+		/// Sticky judgement.
+		StickyJudgement,
+		/// Judgement given.
+		JudgementGiven,
+		/// Invalid judgement.
+		InvalidJudgement,
+		/// The index is invalid.
+		InvalidIndex,
+		/// The target is invalid.
+		InvalidTarget,
+		/// Too many additional fields.
+		TooManyFields,
+		/// Maximum amount of registrars reached. Cannot add any more.
+		TooManyRegistrars,
+		/// Account ID is already named.
+		AlreadyClaimed,
+		/// Sender is not a sub-account.
+		NotSub,
+		/// Sub-account isn't owned by sender.
+		NotOwned,
+		/// The provided judgement was for a different identity.
+		JudgementForDifferentIdentity,
+		/// Error that occurs when there is an issue paying for judgement.
+		JudgementPaymentFailed,
+	}
+
+	#[pallet::event]
+	#[pallet::generate_deposit(pub(super) fn deposit_event)]
+	pub enum Event<T: Config> {
+		/// A name was set or reset (which will remove all judgements).
+		IdentitySet { who: T::AccountId },
+		/// A name was cleared, and the given balance returned.
+		IdentityCleared {
+			who: T::AccountId,
+			deposit: BalanceOf<T>,
+		},
+		/// A name was removed and the given balance slashed.
+		IdentityKilled {
+			who: T::AccountId,
+			deposit: BalanceOf<T>,
+		},
+		/// A number of identities and associated info were forcibly inserted.
+		IdentitiesInserted { amount: u32 },
+		/// A number of identities and all associated info were forcibly removed.
+		IdentitiesRemoved { amount: u32 },
+		/// A judgement was asked from a registrar.
+		JudgementRequested {
+			who: T::AccountId,
+			registrar_index: RegistrarIndex,
+		},
+		/// A judgement request was retracted.
+		JudgementUnrequested {
+			who: T::AccountId,
+			registrar_index: RegistrarIndex,
+		},
+		/// A judgement was given by a registrar.
+		JudgementGiven {
+			target: T::AccountId,
+			registrar_index: RegistrarIndex,
+		},
+		/// A registrar was added.
+		RegistrarAdded { registrar_index: RegistrarIndex },
+		/// A sub-identity was added to an identity and the deposit paid.
+		SubIdentityAdded {
+			sub: T::AccountId,
+			main: T::AccountId,
+			deposit: BalanceOf<T>,
+		},
+		/// A sub-identity was removed from an identity and the deposit freed.
+		SubIdentityRemoved {
+			sub: T::AccountId,
+			main: T::AccountId,
+			deposit: BalanceOf<T>,
+		},
+		/// A sub-identity was cleared, and the given deposit repatriated from the
+		/// main identity account to the sub-identity account.
+		SubIdentityRevoked {
+			sub: T::AccountId,
+			main: T::AccountId,
+			deposit: BalanceOf<T>,
+		},
+	}
+
+	#[pallet::call]
+	/// Identity pallet declaration.
+	impl<T: Config> Pallet<T> {
+		/// Add a registrar to the system.
+		///
+		/// The dispatch origin for this call must be `T::RegistrarOrigin`.
+		///
+		/// - `account`: the account of the registrar.
+		///
+		/// Emits `RegistrarAdded` if successful.
+		///
+		/// # <weight>
+		/// - `O(R)` where `R` registrar-count (governance-bounded and code-bounded).
+		/// - One storage mutation (codec `O(R)`).
+		/// - One event.
+		/// # </weight>
+		#[pallet::call_index(0)]
+		#[pallet::weight(T::WeightInfo::add_registrar(T::MaxRegistrars::get()))]
+		pub fn add_registrar(
+			origin: OriginFor<T>,
+			account: AccountIdLookupOf<T>,
+		) -> DispatchResultWithPostInfo {
+			T::RegistrarOrigin::ensure_origin(origin)?;
+			let account = T::Lookup::lookup(account)?;
+
+			let (i, registrar_count) = <Registrars<T>>::try_mutate(
+				|registrars| -> Result<(RegistrarIndex, usize), DispatchError> {
+					registrars
+						.try_push(Some(RegistrarInfo {
+							account,
+							fee: Zero::zero(),
+							fields: Default::default(),
+						}))
+						.map_err(|_| Error::<T>::TooManyRegistrars)?;
+					Ok(((registrars.len() - 1) as RegistrarIndex, registrars.len()))
+				},
+			)?;
+
+			Self::deposit_event(Event::RegistrarAdded { registrar_index: i });
+
+			Ok(Some(T::WeightInfo::add_registrar(registrar_count as u32)).into())
+		}
+
+		/// Set an account's identity information and reserve the appropriate deposit.
+		///
+		/// If the account already has identity information, the deposit is taken as part payment
+		/// for the new deposit.
+		///
+		/// The dispatch origin for this call must be _Signed_.
+		///
+		/// - `info`: The identity information.
+		///
+		/// Emits `IdentitySet` if successful.
+		///
+		/// # <weight>
+		/// - `O(X + X' + R)`
+		///   - where `X` additional-field-count (deposit-bounded and code-bounded)
+		///   - where `R` judgements-count (registrar-count-bounded)
+		/// - One balance reserve operation.
+		/// - One storage mutation (codec-read `O(X' + R)`, codec-write `O(X + R)`).
+		/// - One event.
+		/// # </weight>
+		#[pallet::call_index(1)]
+		#[pallet::weight( T::WeightInfo::set_identity(
+			T::MaxRegistrars::get(), // R
+			T::MaxAdditionalFields::get(), // X
+		))]
+		pub fn set_identity(
+			origin: OriginFor<T>,
+			info: Box<IdentityInfo<T::MaxAdditionalFields>>,
+		) -> DispatchResultWithPostInfo {
+			let sender = ensure_signed(origin)?;
+			let extra_fields = info.additional.len() as u32;
+			ensure!(
+				extra_fields <= T::MaxAdditionalFields::get(),
+				Error::<T>::TooManyFields
+			);
+			let fd = <BalanceOf<T>>::from(extra_fields) * T::FieldDeposit::get();
+
+			let mut id = match <IdentityOf<T>>::get(&sender) {
+				Some(mut id) => {
+					// Only keep non-positive judgements.
+					id.judgements.retain(|j| j.1.is_sticky());
+					id.info = *info;
+					id
+				}
+				None => Registration {
+					info: *info,
+					judgements: BoundedVec::default(),
+					deposit: Zero::zero(),
+				},
+			};
+
+			let old_deposit = id.deposit;
+			id.deposit = T::BasicDeposit::get() + fd;
+			if id.deposit > old_deposit {
+				T::Currency::reserve(&sender, id.deposit - old_deposit)?;
+			}
+			if old_deposit > id.deposit {
+				let err_amount = T::Currency::unreserve(&sender, old_deposit - id.deposit);
+				debug_assert!(err_amount.is_zero());
+			}
+
+			let judgements = id.judgements.len();
+			<IdentityOf<T>>::insert(&sender, id);
+			Self::deposit_event(Event::IdentitySet { who: sender });
+
+			Ok(Some(T::WeightInfo::set_identity(
+				judgements as u32, // R
+				extra_fields,      // X
+			))
+			.into())
+		}
+
+		/// Set the sub-accounts of the sender.
+		///
+		/// Payment: Any aggregate balance reserved by previous `set_subs` calls will be returned
+		/// and an amount `SubAccountDeposit` will be reserved for each item in `subs`.
+		///
+		/// The dispatch origin for this call must be _Signed_ and the sender must have a registered
+		/// identity.
+		///
+		/// - `subs`: The identity's (new) sub-accounts.
+		///
+		/// # <weight>
+		/// - `O(P + S)`
+		///   - where `P` old-subs-count (hard- and deposit-bounded).
+		///   - where `S` subs-count (hard- and deposit-bounded).
+		/// - At most one balance operations.
+		/// - DB:
+		///   - `P + S` storage mutations (codec complexity `O(1)`)
+		///   - One storage read (codec complexity `O(P)`).
+		///   - One storage write (codec complexity `O(S)`).
+		///   - One storage-exists (`IdentityOf::contains_key`).
+		/// # </weight>
+		// TODO: This whole extrinsic screams "not optimized". For example we could
+		// filter any overlap between new and old subs, and avoid reading/writing
+		// to those values... We could also ideally avoid needing to write to
+		// N storage items for N sub accounts. Right now the weight on this function
+		// is a large overestimate due to the fact that it could potentially write
+		// to 2 x T::MaxSubAccounts::get().
+		#[pallet::call_index(2)]
+		#[pallet::weight(T::WeightInfo::set_subs_old(T::MaxSubAccounts::get()) // P: Assume max sub accounts removed.
+			.saturating_add(T::WeightInfo::set_subs_new(subs.len() as u32)) // S: Assume all subs are new.
+		)]
+		pub fn set_subs(
+			origin: OriginFor<T>,
+			subs: Vec<(T::AccountId, Data)>,
+		) -> DispatchResultWithPostInfo {
+			let sender = ensure_signed(origin)?;
+			ensure!(<IdentityOf<T>>::contains_key(&sender), Error::<T>::NotFound);
+			ensure!(
+				subs.len() <= T::MaxSubAccounts::get() as usize,
+				Error::<T>::TooManySubAccounts
+			);
+
+			let (old_deposit, old_ids) = <SubsOf<T>>::get(&sender);
+			let new_deposit = T::SubAccountDeposit::get() * <BalanceOf<T>>::from(subs.len() as u32);
+
+			let not_other_sub = subs
+				.iter()
+				.filter_map(|i| SuperOf::<T>::get(&i.0))
+				.all(|i| i.0 == sender);
+			ensure!(not_other_sub, Error::<T>::AlreadyClaimed);
+
+			if old_deposit < new_deposit {
+				T::Currency::reserve(&sender, new_deposit - old_deposit)?;
+			} else if old_deposit > new_deposit {
+				let err_amount = T::Currency::unreserve(&sender, old_deposit - new_deposit);
+				debug_assert!(err_amount.is_zero());
+			}
+			// do nothing if they're equal.
+
+			for s in old_ids.iter() {
+				<SuperOf<T>>::remove(s);
+			}
+			let mut ids = BoundedVec::<T::AccountId, T::MaxSubAccounts>::default();
+			for (id, name) in subs {
+				<SuperOf<T>>::insert(&id, (sender.clone(), name));
+				ids.try_push(id)
+					.expect("subs length is less than T::MaxSubAccounts; qed");
+			}
+			let new_subs = ids.len();
+
+			if ids.is_empty() {
+				<SubsOf<T>>::remove(&sender);
+			} else {
+				<SubsOf<T>>::insert(&sender, (new_deposit, ids));
+			}
+
+			Ok(Some(
+				T::WeightInfo::set_subs_old(old_ids.len() as u32) // P: Real number of old accounts removed.
+					// S: New subs added
+					.saturating_add(T::WeightInfo::set_subs_new(new_subs as u32)),
+			)
+			.into())
+		}
+
+		/// Clear an account's identity info and all sub-accounts and return all deposits.
+		///
+		/// Payment: All reserved balances on the account are returned.
+		///
+		/// The dispatch origin for this call must be _Signed_ and the sender must have a registered
+		/// identity.
+		///
+		/// Emits `IdentityCleared` if successful.
+		///
+		/// # <weight>
+		/// - `O(R + S + X)`
+		///   - where `R` registrar-count (governance-bounded).
+		///   - where `S` subs-count (hard- and deposit-bounded).
+		///   - where `X` additional-field-count (deposit-bounded and code-bounded).
+		/// - One balance-unreserve operation.
+		/// - `2` storage reads and `S + 2` storage deletions.
+		/// - One event.
+		/// # </weight>
+		#[pallet::call_index(3)]
+		#[pallet::weight(T::WeightInfo::clear_identity(
+			T::MaxRegistrars::get(), // R
+			T::MaxSubAccounts::get(), // S
+			T::MaxAdditionalFields::get(), // X
+		))]
+		pub fn clear_identity(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
+			let sender = ensure_signed(origin)?;
+
+			let (subs_deposit, sub_ids) = <SubsOf<T>>::take(&sender);
+			let id = <IdentityOf<T>>::take(&sender).ok_or(Error::<T>::NotNamed)?;
+			let deposit = id.total_deposit() + subs_deposit;
+			for sub in sub_ids.iter() {
+				<SuperOf<T>>::remove(sub);
+			}
+
+			let err_amount = T::Currency::unreserve(&sender, deposit);
+			debug_assert!(err_amount.is_zero());
+
+			Self::deposit_event(Event::IdentityCleared {
+				who: sender,
+				deposit,
+			});
+
+			Ok(Some(T::WeightInfo::clear_identity(
+				id.judgements.len() as u32,      // R
+				sub_ids.len() as u32,            // S
+				id.info.additional.len() as u32, // X
+			))
+			.into())
+		}
+
+		/// Request a judgement from a registrar.
+		///
+		/// Payment: At most `max_fee` will be reserved for payment to the registrar if judgement
+		/// given.
+		///
+		/// The dispatch origin for this call must be _Signed_ and the sender must have a
+		/// registered identity.
+		///
+		/// - `reg_index`: The index of the registrar whose judgement is requested.
+		/// - `max_fee`: The maximum fee that may be paid. This should just be auto-populated as:
+		///
+		/// ```nocompile
+		/// Self::registrars().get(reg_index).unwrap().fee
+		/// ```
+		///
+		/// Emits `JudgementRequested` if successful.
+		///
+		/// # <weight>
+		/// - `O(R + X)`.
+		/// - One balance-reserve operation.
+		/// - Storage: 1 read `O(R)`, 1 mutate `O(X + R)`.
+		/// - One event.
+		/// # </weight>
+		#[pallet::call_index(4)]
+		#[pallet::weight(T::WeightInfo::request_judgement(
+			T::MaxRegistrars::get(), // R
+			T::MaxAdditionalFields::get(), // X
+		))]
+		pub fn request_judgement(
+			origin: OriginFor<T>,
+			#[pallet::compact] reg_index: RegistrarIndex,
+			#[pallet::compact] max_fee: BalanceOf<T>,
+		) -> DispatchResultWithPostInfo {
+			let sender = ensure_signed(origin)?;
+			let registrars = <Registrars<T>>::get();
+			let registrar = registrars
+				.get(reg_index as usize)
+				.and_then(Option::as_ref)
+				.ok_or(Error::<T>::EmptyIndex)?;
+			ensure!(max_fee >= registrar.fee, Error::<T>::FeeChanged);
+			let mut id = <IdentityOf<T>>::get(&sender).ok_or(Error::<T>::NoIdentity)?;
+
+			let item = (reg_index, Judgement::FeePaid(registrar.fee));
+			match id.judgements.binary_search_by_key(&reg_index, |x| x.0) {
+				Ok(i) => {
+					if id.judgements[i].1.is_sticky() {
+						return Err(Error::<T>::StickyJudgement.into());
+					} else {
+						id.judgements[i] = item
+					}
+				}
+				Err(i) => id
+					.judgements
+					.try_insert(i, item)
+					.map_err(|_| Error::<T>::TooManyRegistrars)?,
+			}
+
+			T::Currency::reserve(&sender, registrar.fee)?;
+
+			let judgements = id.judgements.len();
+			let extra_fields = id.info.additional.len();
+			<IdentityOf<T>>::insert(&sender, id);
+
+			Self::deposit_event(Event::JudgementRequested {
+				who: sender,
+				registrar_index: reg_index,
+			});
+
+			Ok(Some(T::WeightInfo::request_judgement(
+				judgements as u32,
+				extra_fields as u32,
+			))
+			.into())
+		}
+
+		/// Cancel a previous request.
+		///
+		/// Payment: A previously reserved deposit is returned on success.
+		///
+		/// The dispatch origin for this call must be _Signed_ and the sender must have a
+		/// registered identity.
+		///
+		/// - `reg_index`: The index of the registrar whose judgement is no longer requested.
+		///
+		/// Emits `JudgementUnrequested` if successful.
+		///
+		/// # <weight>
+		/// - `O(R + X)`.
+		/// - One balance-reserve operation.
+		/// - One storage mutation `O(R + X)`.
+		/// - One event
+		/// # </weight>
+		#[pallet::call_index(5)]
+		#[pallet::weight(T::WeightInfo::cancel_request(
+			T::MaxRegistrars::get(), // R
+			T::MaxAdditionalFields::get(), // X
+		))]
+		pub fn cancel_request(
+			origin: OriginFor<T>,
+			reg_index: RegistrarIndex,
+		) -> DispatchResultWithPostInfo {
+			let sender = ensure_signed(origin)?;
+			let mut id = <IdentityOf<T>>::get(&sender).ok_or(Error::<T>::NoIdentity)?;
+
+			let pos = id
+				.judgements
+				.binary_search_by_key(&reg_index, |x| x.0)
+				.map_err(|_| Error::<T>::NotFound)?;
+			let fee = if let Judgement::FeePaid(fee) = id.judgements.remove(pos).1 {
+				fee
+			} else {
+				return Err(Error::<T>::JudgementGiven.into());
+			};
+
+			let err_amount = T::Currency::unreserve(&sender, fee);
+			debug_assert!(err_amount.is_zero());
+			let judgements = id.judgements.len();
+			let extra_fields = id.info.additional.len();
+			<IdentityOf<T>>::insert(&sender, id);
+
+			Self::deposit_event(Event::JudgementUnrequested {
+				who: sender,
+				registrar_index: reg_index,
+			});
+
+			Ok(Some(T::WeightInfo::cancel_request(
+				judgements as u32,
+				extra_fields as u32,
+			))
+			.into())
+		}
+
+		/// Set the fee required for a judgement to be requested from a registrar.
+		///
+		/// The dispatch origin for this call must be _Signed_ and the sender must be the account
+		/// of the registrar whose index is `index`.
+		///
+		/// - `index`: the index of the registrar whose fee is to be set.
+		/// - `fee`: the new fee.
+		///
+		/// # <weight>
+		/// - `O(R)`.
+		/// - One storage mutation `O(R)`.
+		/// - Benchmark: 7.315 + R * 0.329 µs (min squares analysis)
+		/// # </weight>
+		#[pallet::call_index(6)]
+		#[pallet::weight(T::WeightInfo::set_fee(T::MaxRegistrars::get()))] // R
+		pub fn set_fee(
+			origin: OriginFor<T>,
+			#[pallet::compact] index: RegistrarIndex,
+			#[pallet::compact] fee: BalanceOf<T>,
+		) -> DispatchResultWithPostInfo {
+			let who = ensure_signed(origin)?;
+
+			let registrars = <Registrars<T>>::mutate(|rs| -> Result<usize, DispatchError> {
+				rs.get_mut(index as usize)
+					.and_then(|x| x.as_mut())
+					.and_then(|r| {
+						if r.account == who {
+							r.fee = fee;
+							Some(())
+						} else {
+							None
+						}
+					})
+					.ok_or_else(|| DispatchError::from(Error::<T>::InvalidIndex))?;
+				Ok(rs.len())
+			})?;
+			Ok(Some(T::WeightInfo::set_fee(registrars as u32)).into()) // R
+		}
+
+		/// Change the account associated with a registrar.
+		///
+		/// The dispatch origin for this call must be _Signed_ and the sender must be the account
+		/// of the registrar whose index is `index`.
+		///
+		/// - `index`: the index of the registrar whose fee is to be set.
+		/// - `new`: the new account ID.
+		///
+		/// # <weight>
+		/// - `O(R)`.
+		/// - One storage mutation `O(R)`.
+		/// - Benchmark: 8.823 + R * 0.32 µs (min squares analysis)
+		/// # </weight>
+		#[pallet::call_index(7)]
+		#[pallet::weight(T::WeightInfo::set_account_id(T::MaxRegistrars::get()))] // R
+		pub fn set_account_id(
+			origin: OriginFor<T>,
+			#[pallet::compact] index: RegistrarIndex,
+			new: AccountIdLookupOf<T>,
+		) -> DispatchResultWithPostInfo {
+			let who = ensure_signed(origin)?;
+			let new = T::Lookup::lookup(new)?;
+
+			let registrars = <Registrars<T>>::mutate(|rs| -> Result<usize, DispatchError> {
+				rs.get_mut(index as usize)
+					.and_then(|x| x.as_mut())
+					.and_then(|r| {
+						if r.account == who {
+							r.account = new;
+							Some(())
+						} else {
+							None
+						}
+					})
+					.ok_or_else(|| DispatchError::from(Error::<T>::InvalidIndex))?;
+				Ok(rs.len())
+			})?;
+			Ok(Some(T::WeightInfo::set_account_id(registrars as u32)).into()) // R
+		}
+
+		/// Set the field information for a registrar.
+		///
+		/// The dispatch origin for this call must be _Signed_ and the sender must be the account
+		/// of the registrar whose index is `index`.
+		///
+		/// - `index`: the index of the registrar whose fee is to be set.
+		/// - `fields`: the fields that the registrar concerns themselves with.
+		///
+		/// # <weight>
+		/// - `O(R)`.
+		/// - One storage mutation `O(R)`.
+		/// - Benchmark: 7.464 + R * 0.325 µs (min squares analysis)
+		/// # </weight>
+		#[pallet::call_index(8)]
+		#[pallet::weight(T::WeightInfo::set_fields(T::MaxRegistrars::get()))] // R
+		pub fn set_fields(
+			origin: OriginFor<T>,
+			#[pallet::compact] index: RegistrarIndex,
+			fields: IdentityFields,
+		) -> DispatchResultWithPostInfo {
+			let who = ensure_signed(origin)?;
+
+			let registrars = <Registrars<T>>::mutate(|rs| -> Result<usize, DispatchError> {
+				rs.get_mut(index as usize)
+					.and_then(|x| x.as_mut())
+					.and_then(|r| {
+						if r.account == who {
+							r.fields = fields;
+							Some(())
+						} else {
+							None
+						}
+					})
+					.ok_or_else(|| DispatchError::from(Error::<T>::InvalidIndex))?;
+				Ok(rs.len())
+			})?;
+			Ok(Some(T::WeightInfo::set_fields(
+				registrars as u32, // R
+			))
+			.into())
+		}
+
+		/// Provide a judgement for an account's identity.
+		///
+		/// The dispatch origin for this call must be _Signed_ and the sender must be the account
+		/// of the registrar whose index is `reg_index`.
+		///
+		/// - `reg_index`: the index of the registrar whose judgement is being made.
+		/// - `target`: the account whose identity the judgement is upon. This must be an account
+		///   with a registered identity.
+		/// - `judgement`: the judgement of the registrar of index `reg_index` about `target`.
+		/// - `identity`: The hash of the [`IdentityInfo`] for that the judgement is provided.
+		///
+		/// Emits `JudgementGiven` if successful.
+		///
+		/// # <weight>
+		/// - `O(R + X)`.
+		/// - One balance-transfer operation.
+		/// - Up to one account-lookup operation.
+		/// - Storage: 1 read `O(R)`, 1 mutate `O(R + X)`.
+		/// - One event.
+		/// # </weight>
+		#[pallet::call_index(9)]
+		#[pallet::weight(T::WeightInfo::provide_judgement(
+			T::MaxRegistrars::get(), // R
+			T::MaxAdditionalFields::get(), // X
+		))]
+		pub fn provide_judgement(
+			origin: OriginFor<T>,
+			#[pallet::compact] reg_index: RegistrarIndex,
+			target: AccountIdLookupOf<T>,
+			judgement: Judgement<BalanceOf<T>>,
+			identity: T::Hash,
+		) -> DispatchResultWithPostInfo {
+			let sender = ensure_signed(origin)?;
+			let target = T::Lookup::lookup(target)?;
+			ensure!(!judgement.has_deposit(), Error::<T>::InvalidJudgement);
+			<Registrars<T>>::get()
+				.get(reg_index as usize)
+				.and_then(Option::as_ref)
+				.filter(|r| r.account == sender)
+				.ok_or(Error::<T>::InvalidIndex)?;
+			let mut id = <IdentityOf<T>>::get(&target).ok_or(Error::<T>::InvalidTarget)?;
+
+			if T::Hashing::hash_of(&id.info) != identity {
+				return Err(Error::<T>::JudgementForDifferentIdentity.into());
+			}
+
+			let item = (reg_index, judgement);
+			match id.judgements.binary_search_by_key(&reg_index, |x| x.0) {
+				Ok(position) => {
+					if let Judgement::FeePaid(fee) = id.judgements[position].1 {
+						T::Currency::repatriate_reserved(
+							&target,
+							&sender,
+							fee,
+							BalanceStatus::Free,
+						)
+						.map_err(|_| Error::<T>::JudgementPaymentFailed)?;
+					}
+					id.judgements[position] = item
+				}
+				Err(position) => id
+					.judgements
+					.try_insert(position, item)
+					.map_err(|_| Error::<T>::TooManyRegistrars)?,
+			}
+
+			let judgements = id.judgements.len();
+			let extra_fields = id.info.additional.len();
+			<IdentityOf<T>>::insert(&target, id);
+			Self::deposit_event(Event::JudgementGiven {
+				target,
+				registrar_index: reg_index,
+			});
+
+			Ok(Some(T::WeightInfo::provide_judgement(
+				judgements as u32,
+				extra_fields as u32,
+			))
+			.into())
+		}
+
+		/// Remove an account's identity and sub-account information and slash the deposits.
+		///
+		/// Payment: Reserved balances from `set_subs` and `set_identity` are slashed and handled by
+		/// `Slash`. Verification request deposits are not returned; they should be cancelled
+		/// manually using `cancel_request`.
+		///
+		/// The dispatch origin for this call must match `T::ForceOrigin`.
+		///
+		/// - `target`: the account whose identity the judgement is upon. This must be an account
+		///   with a registered identity.
+		///
+		/// Emits `IdentityKilled` if successful.
+		///
+		/// # <weight>
+		/// - `O(R + S + X)`.
+		/// - One balance-reserve operation.
+		/// - `S + 2` storage mutations.
+		/// - One event.
+		/// # </weight>
+		#[pallet::call_index(10)]
+		#[pallet::weight(T::WeightInfo::kill_identity(
+			T::MaxRegistrars::get(), // R
+			T::MaxSubAccounts::get(), // S
+			T::MaxAdditionalFields::get(), // X
+		))]
+		pub fn kill_identity(
+			origin: OriginFor<T>,
+			target: AccountIdLookupOf<T>,
+		) -> DispatchResultWithPostInfo {
+			T::ForceOrigin::ensure_origin(origin)?;
+
+			// Figure out who we're meant to be clearing.
+			let target = T::Lookup::lookup(target)?;
+			// Grab their deposit (and check that they have one).
+			let (subs_deposit, sub_ids) = <SubsOf<T>>::take(&target);
+			let id = <IdentityOf<T>>::take(&target).ok_or(Error::<T>::NotNamed)?;
+			let deposit = id.total_deposit() + subs_deposit;
+			for sub in sub_ids.iter() {
+				<SuperOf<T>>::remove(sub);
+			}
+			// Slash their deposit from them.
+			T::Slashed::on_unbalanced(T::Currency::slash_reserved(&target, deposit).0);
+
+			Self::deposit_event(Event::IdentityKilled {
+				who: target,
+				deposit,
+			});
+
+			Ok(Some(T::WeightInfo::kill_identity(
+				id.judgements.len() as u32,      // R
+				sub_ids.len() as u32,            // S
+				id.info.additional.len() as u32, // X
+			))
+			.into())
+		}
+
+		/// Add the given account to the sender's subs.
+		///
+		/// Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated
+		/// to the sender.
+		///
+		/// The dispatch origin for this call must be _Signed_ and the sender must have a registered
+		/// sub identity of `sub`.
+		#[pallet::call_index(11)]
+		#[pallet::weight(T::WeightInfo::add_sub(T::MaxSubAccounts::get()))]
+		pub fn add_sub(
+			origin: OriginFor<T>,
+			sub: AccountIdLookupOf<T>,
+			data: Data,
+		) -> DispatchResult {
+			let sender = ensure_signed(origin)?;
+			let sub = T::Lookup::lookup(sub)?;
+			ensure!(
+				IdentityOf::<T>::contains_key(&sender),
+				Error::<T>::NoIdentity
+			);
+
+			// Check if it's already claimed as sub-identity.
+			ensure!(
+				!SuperOf::<T>::contains_key(&sub),
+				Error::<T>::AlreadyClaimed
+			);
+
+			SubsOf::<T>::try_mutate(&sender, |(ref mut subs_deposit, ref mut sub_ids)| {
+				// Ensure there is space and that the deposit is paid.
+				ensure!(
+					sub_ids.len() < T::MaxSubAccounts::get() as usize,
+					Error::<T>::TooManySubAccounts
+				);
+				let deposit = T::SubAccountDeposit::get();
+				T::Currency::reserve(&sender, deposit)?;
+
+				SuperOf::<T>::insert(&sub, (sender.clone(), data));
+				sub_ids
+					.try_push(sub.clone())
+					.expect("sub ids length checked above; qed");
+				*subs_deposit = subs_deposit.saturating_add(deposit);
+
+				Self::deposit_event(Event::SubIdentityAdded {
+					sub,
+					main: sender.clone(),
+					deposit,
+				});
+				Ok(())
+			})
+		}
+
+		/// Alter the associated name of the given sub-account.
+		///
+		/// The dispatch origin for this call must be _Signed_ and the sender must have a registered
+		/// sub identity of `sub`.
+		#[pallet::call_index(12)]
+		#[pallet::weight(T::WeightInfo::rename_sub(T::MaxSubAccounts::get()))]
+		pub fn rename_sub(
+			origin: OriginFor<T>,
+			sub: AccountIdLookupOf<T>,
+			data: Data,
+		) -> DispatchResult {
+			let sender = ensure_signed(origin)?;
+			let sub = T::Lookup::lookup(sub)?;
+			ensure!(
+				IdentityOf::<T>::contains_key(&sender),
+				Error::<T>::NoIdentity
+			);
+			ensure!(
+				SuperOf::<T>::get(&sub).map_or(false, |x| x.0 == sender),
+				Error::<T>::NotOwned
+			);
+			SuperOf::<T>::insert(&sub, (sender, data));
+			Ok(())
+		}
+
+		/// Remove the given account from the sender's subs.
+		///
+		/// Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated
+		/// to the sender.
+		///
+		/// The dispatch origin for this call must be _Signed_ and the sender must have a registered
+		/// sub identity of `sub`.
+		#[pallet::call_index(13)]
+		#[pallet::weight(T::WeightInfo::remove_sub(T::MaxSubAccounts::get()))]
+		pub fn remove_sub(origin: OriginFor<T>, sub: AccountIdLookupOf<T>) -> DispatchResult {
+			let sender = ensure_signed(origin)?;
+			ensure!(
+				IdentityOf::<T>::contains_key(&sender),
+				Error::<T>::NoIdentity
+			);
+			let sub = T::Lookup::lookup(sub)?;
+			let (sup, _) = SuperOf::<T>::get(&sub).ok_or(Error::<T>::NotSub)?;
+			ensure!(sup == sender, Error::<T>::NotOwned);
+			SuperOf::<T>::remove(&sub);
+			SubsOf::<T>::mutate(&sup, |(ref mut subs_deposit, ref mut sub_ids)| {
+				sub_ids.retain(|x| x != &sub);
+				let deposit = T::SubAccountDeposit::get().min(*subs_deposit);
+				*subs_deposit -= deposit;
+				let err_amount = T::Currency::unreserve(&sender, deposit);
+				debug_assert!(err_amount.is_zero());
+				Self::deposit_event(Event::SubIdentityRemoved {
+					sub,
+					main: sender,
+					deposit,
+				});
+			});
+			Ok(())
+		}
+
+		/// Remove the sender as a sub-account.
+		///
+		/// Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated
+		/// to the sender (*not* the original depositor).
+		///
+		/// The dispatch origin for this call must be _Signed_ and the sender must have a registered
+		/// super-identity.
+		///
+		/// NOTE: This should not normally be used, but is provided in the case that the non-
+		/// controller of an account is maliciously registered as a sub-account.
+		#[pallet::call_index(14)]
+		#[pallet::weight(T::WeightInfo::quit_sub(T::MaxSubAccounts::get()))]
+		pub fn quit_sub(origin: OriginFor<T>) -> DispatchResult {
+			let sender = ensure_signed(origin)?;
+			let (sup, _) = SuperOf::<T>::take(&sender).ok_or(Error::<T>::NotSub)?;
+			SubsOf::<T>::mutate(&sup, |(ref mut subs_deposit, ref mut sub_ids)| {
+				sub_ids.retain(|x| x != &sender);
+				let deposit = T::SubAccountDeposit::get().min(*subs_deposit);
+				*subs_deposit -= deposit;
+				let _ =
+					T::Currency::repatriate_reserved(&sup, &sender, deposit, BalanceStatus::Free);
+				Self::deposit_event(Event::SubIdentityRevoked {
+					sub: sender,
+					main: sup.clone(),
+					deposit,
+				});
+			});
+			Ok(())
+		}
+
+		/// Set identities to be associated with the provided accounts as force origin.
+		///
+		/// This is not meant to operate in tandem with the identity pallet as is,
+		/// and be instead used to keep identities made and verified externally,
+		/// forbidden from interacting with an ordinary user, since it ignores any safety mechanism.
+		#[pallet::call_index(15)]
+		#[pallet::weight(T::WeightInfo::force_insert_identities(
+			T::MaxAdditionalFields::get(), // X
+			identities.len() as u32, // N
+		))]
+		pub fn force_insert_identities(
+			origin: OriginFor<T>,
+			identities: Vec<(
+				T::AccountId,
+				Registration<BalanceOf<T>, T::MaxRegistrars, T::MaxAdditionalFields>,
+			)>,
+		) -> DispatchResult {
+			T::ForceOrigin::ensure_origin(origin)?;
+			for identity in identities.clone() {
+				IdentityOf::<T>::insert(identity.0, identity.1);
+			}
+			Self::deposit_event(Event::IdentitiesInserted {
+				amount: identities.len() as u32,
+			});
+			Ok(())
+		}
+
+		/// Remove identities associated with the provided accounts as force origin.
+		///
+		/// This is not meant to operate in tandem with the identity pallet as is,
+		/// and be instead used to keep identities made and verified externally,
+		/// forbidden from interacting with an ordinary user, since it ignores any safety mechanism.
+		#[pallet::call_index(16)]
+		#[pallet::weight(T::WeightInfo::force_remove_identities(
+			T::MaxAdditionalFields::get(), // X
+			identities.len() as u32, // N
+		))]
+		pub fn force_remove_identities(
+			origin: OriginFor<T>,
+			identities: Vec<T::AccountId>,
+		) -> DispatchResult {
+			T::ForceOrigin::ensure_origin(origin)?;
+			for identity in identities.clone() {
+				IdentityOf::<T>::set(identity, None);
+			}
+			Self::deposit_event(Event::IdentitiesRemoved {
+				amount: identities.len() as u32,
+			});
+			Ok(())
+		}
+	}
+}
+
+impl<T: Config> Pallet<T> {
+	/// Get the subs of an account.
+	pub fn subs(who: &T::AccountId) -> Vec<(T::AccountId, Data)> {
+		SubsOf::<T>::get(who)
+			.1
+			.into_iter()
+			.filter_map(|a| SuperOf::<T>::get(&a).map(|x| (a, x.1)))
+			.collect()
+	}
+
+	/// Check if the account has corresponding identity information by the identity field.
+	pub fn has_identity(who: &T::AccountId, fields: u64) -> bool {
+		IdentityOf::<T>::get(who).map_or(false, |registration| {
+			(registration.info.fields().0.bits() & fields) == fields
+		})
+	}
+}
addedpallets/identity/src/tests.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/identity/src/tests.rs
@@ -0,0 +1,755 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+// Original license:
+// 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 Identity Pallet
+
+use super::*;
+use crate as pallet_identity;
+
+use codec::{Decode, Encode};
+use frame_support::{
+	assert_noop, assert_ok, ord_parameter_types, parameter_types,
+	traits::{ConstU32, ConstU64, EitherOfDiverse},
+	BoundedVec,
+};
+use frame_system::{EnsureRoot, EnsureSignedBy};
+use sp_core::H256;
+use sp_runtime::{
+	testing::Header,
+	traits::{BadOrigin, BlakeTwo256, IdentityLookup},
+};
+
+type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
+type Block = frame_system::mocking::MockBlock<Test>;
+
+frame_support::construct_runtime!(
+	pub enum Test where
+		Block = Block,
+		NodeBlock = Block,
+		UncheckedExtrinsic = UncheckedExtrinsic,
+	{
+		System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
+		Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
+		Identity: pallet_identity::{Pallet, Call, Storage, Event<T>},
+	}
+);
+
+parameter_types! {
+	pub BlockWeights: frame_system::limits::BlockWeights =
+		frame_system::limits::BlockWeights::simple_max(frame_support::weights::Weight::from_ref_time(1024));
+}
+impl frame_system::Config for Test {
+	type BaseCallFilter = frame_support::traits::Everything;
+	type BlockWeights = ();
+	type BlockLength = ();
+	type RuntimeOrigin = RuntimeOrigin;
+	type Index = u64;
+	type BlockNumber = u64;
+	type Hash = H256;
+	type RuntimeCall = RuntimeCall;
+	type Hashing = BlakeTwo256;
+	type AccountId = u64;
+	type Lookup = IdentityLookup<Self::AccountId>;
+	type Header = Header;
+	type RuntimeEvent = RuntimeEvent;
+	type BlockHashCount = ConstU64<250>;
+	type DbWeight = ();
+	type Version = ();
+	type PalletInfo = PalletInfo;
+	type AccountData = pallet_balances::AccountData<u64>;
+	type OnNewAccount = ();
+	type OnKilledAccount = ();
+	type SystemWeightInfo = ();
+	type SS58Prefix = ();
+	type OnSetCode = ();
+	type MaxConsumers = ConstU32<16>;
+}
+
+impl pallet_balances::Config for Test {
+	type Balance = u64;
+	type RuntimeEvent = RuntimeEvent;
+	type DustRemoval = ();
+	type ExistentialDeposit = ConstU64<1>;
+	type AccountStore = System;
+	type MaxLocks = ();
+	type MaxReserves = ();
+	type ReserveIdentifier = [u8; 8];
+	type WeightInfo = ();
+}
+
+parameter_types! {
+	pub const MaxAdditionalFields: u32 = 2;
+	pub const MaxRegistrars: u32 = 20;
+}
+
+ord_parameter_types! {
+	pub const One: u64 = 1;
+	pub const Two: u64 = 2;
+}
+type EnsureOneOrRoot = EitherOfDiverse<EnsureRoot<u64>, EnsureSignedBy<One, u64>>;
+type EnsureTwoOrRoot = EitherOfDiverse<EnsureRoot<u64>, EnsureSignedBy<Two, u64>>;
+impl pallet_identity::Config for Test {
+	type RuntimeEvent = RuntimeEvent;
+	type Currency = Balances;
+	type Slashed = ();
+	type BasicDeposit = ConstU64<10>;
+	type FieldDeposit = ConstU64<10>;
+	type SubAccountDeposit = ConstU64<10>;
+	type MaxSubAccounts = ConstU32<2>;
+	type MaxAdditionalFields = MaxAdditionalFields;
+	type MaxRegistrars = MaxRegistrars;
+	type RegistrarOrigin = EnsureOneOrRoot;
+	type ForceOrigin = EnsureTwoOrRoot;
+	type WeightInfo = ();
+}
+
+pub fn new_test_ext() -> sp_io::TestExternalities {
+	let mut t = frame_system::GenesisConfig::default()
+		.build_storage::<Test>()
+		.unwrap();
+	pallet_balances::GenesisConfig::<Test> {
+		balances: vec![(1, 10), (2, 10), (3, 10), (10, 100), (20, 100), (30, 100)],
+	}
+	.assimilate_storage(&mut t)
+	.unwrap();
+	t.into()
+}
+
+fn ten() -> IdentityInfo<MaxAdditionalFields> {
+	IdentityInfo {
+		display: Data::Raw(b"ten".to_vec().try_into().unwrap()),
+		legal: Data::Raw(b"The Right Ordinal Ten, Esq.".to_vec().try_into().unwrap()),
+		..Default::default()
+	}
+}
+
+fn twenty() -> IdentityInfo<MaxAdditionalFields> {
+	IdentityInfo {
+		display: Data::Raw(b"twenty".to_vec().try_into().unwrap()),
+		legal: Data::Raw(
+			b"The Right Ordinal Twenty, Esq."
+				.to_vec()
+				.try_into()
+				.unwrap(),
+		),
+		..Default::default()
+	}
+}
+
+#[test]
+fn editing_subaccounts_should_work() {
+	new_test_ext().execute_with(|| {
+		let data = |x| Data::Raw(vec![x; 1].try_into().unwrap());
+
+		assert_noop!(
+			Identity::add_sub(RuntimeOrigin::signed(10), 20, data(1)),
+			Error::<Test>::NoIdentity
+		);
+
+		assert_ok!(Identity::set_identity(
+			RuntimeOrigin::signed(10),
+			Box::new(ten())
+		));
+
+		// first sub account
+		assert_ok!(Identity::add_sub(RuntimeOrigin::signed(10), 1, data(1)));
+		assert_eq!(SuperOf::<Test>::get(1), Some((10, data(1))));
+		assert_eq!(Balances::free_balance(10), 80);
+
+		// second sub account
+		assert_ok!(Identity::add_sub(RuntimeOrigin::signed(10), 2, data(2)));
+		assert_eq!(SuperOf::<Test>::get(1), Some((10, data(1))));
+		assert_eq!(SuperOf::<Test>::get(2), Some((10, data(2))));
+		assert_eq!(Balances::free_balance(10), 70);
+
+		// third sub account is too many
+		assert_noop!(
+			Identity::add_sub(RuntimeOrigin::signed(10), 3, data(3)),
+			Error::<Test>::TooManySubAccounts
+		);
+
+		// rename first sub account
+		assert_ok!(Identity::rename_sub(RuntimeOrigin::signed(10), 1, data(11)));
+		assert_eq!(SuperOf::<Test>::get(1), Some((10, data(11))));
+		assert_eq!(SuperOf::<Test>::get(2), Some((10, data(2))));
+		assert_eq!(Balances::free_balance(10), 70);
+
+		// remove first sub account
+		assert_ok!(Identity::remove_sub(RuntimeOrigin::signed(10), 1));
+		assert_eq!(SuperOf::<Test>::get(1), None);
+		assert_eq!(SuperOf::<Test>::get(2), Some((10, data(2))));
+		assert_eq!(Balances::free_balance(10), 80);
+
+		// add third sub account
+		assert_ok!(Identity::add_sub(RuntimeOrigin::signed(10), 3, data(3)));
+		assert_eq!(SuperOf::<Test>::get(1), None);
+		assert_eq!(SuperOf::<Test>::get(2), Some((10, data(2))));
+		assert_eq!(SuperOf::<Test>::get(3), Some((10, data(3))));
+		assert_eq!(Balances::free_balance(10), 70);
+	});
+}
+
+#[test]
+fn resolving_subaccount_ownership_works() {
+	new_test_ext().execute_with(|| {
+		let data = |x| Data::Raw(vec![x; 1].try_into().unwrap());
+
+		assert_ok!(Identity::set_identity(
+			RuntimeOrigin::signed(10),
+			Box::new(ten())
+		));
+		assert_ok!(Identity::set_identity(
+			RuntimeOrigin::signed(20),
+			Box::new(twenty())
+		));
+
+		// 10 claims 1 as a subaccount
+		assert_ok!(Identity::add_sub(RuntimeOrigin::signed(10), 1, data(1)));
+		assert_eq!(Balances::free_balance(1), 10);
+		assert_eq!(Balances::free_balance(10), 80);
+		assert_eq!(Balances::reserved_balance(10), 20);
+		// 20 cannot claim 1 now
+		assert_noop!(
+			Identity::add_sub(RuntimeOrigin::signed(20), 1, data(1)),
+			Error::<Test>::AlreadyClaimed
+		);
+		// 1 wants to be with 20 so it quits from 10
+		assert_ok!(Identity::quit_sub(RuntimeOrigin::signed(1)));
+		// 1 gets the 10 that 10 paid.
+		assert_eq!(Balances::free_balance(1), 20);
+		assert_eq!(Balances::free_balance(10), 80);
+		assert_eq!(Balances::reserved_balance(10), 10);
+		// 20 can claim 1 now
+		assert_ok!(Identity::add_sub(RuntimeOrigin::signed(20), 1, data(1)));
+	});
+}
+
+#[test]
+fn trailing_zeros_decodes_into_default_data() {
+	let encoded = Data::Raw(b"Hello".to_vec().try_into().unwrap()).encode();
+	assert!(<(Data, Data)>::decode(&mut &encoded[..]).is_err());
+	let input = &mut &encoded[..];
+	let (a, b) = <(Data, Data)>::decode(&mut AppendZerosInput::new(input)).unwrap();
+	assert_eq!(a, Data::Raw(b"Hello".to_vec().try_into().unwrap()));
+	assert_eq!(b, Data::None);
+}
+
+#[test]
+fn adding_registrar_should_work() {
+	new_test_ext().execute_with(|| {
+		assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 3));
+		assert_ok!(Identity::set_fee(RuntimeOrigin::signed(3), 0, 10));
+		let fields = IdentityFields(IdentityField::Display | IdentityField::Legal);
+		assert_ok!(Identity::set_fields(RuntimeOrigin::signed(3), 0, fields));
+		assert_eq!(
+			Identity::registrars(),
+			vec![Some(RegistrarInfo {
+				account: 3,
+				fee: 10,
+				fields
+			})]
+		);
+	});
+}
+
+#[test]
+fn amount_of_registrars_is_limited() {
+	new_test_ext().execute_with(|| {
+		for i in 1..MaxRegistrars::get() + 1 {
+			assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), i as u64));
+		}
+		let last_registrar = MaxRegistrars::get() as u64 + 1;
+		assert_noop!(
+			Identity::add_registrar(RuntimeOrigin::signed(1), last_registrar),
+			Error::<Test>::TooManyRegistrars
+		);
+	});
+}
+
+#[test]
+fn registration_should_work() {
+	new_test_ext().execute_with(|| {
+		assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 3));
+		assert_ok!(Identity::set_fee(RuntimeOrigin::signed(3), 0, 10));
+		let mut three_fields = ten();
+		three_fields
+			.additional
+			.try_push(Default::default())
+			.unwrap();
+		three_fields
+			.additional
+			.try_push(Default::default())
+			.unwrap();
+		assert!(three_fields
+			.additional
+			.try_push(Default::default())
+			.is_err());
+		assert_ok!(Identity::set_identity(
+			RuntimeOrigin::signed(10),
+			Box::new(ten())
+		));
+		assert_eq!(Identity::identity(10).unwrap().info, ten());
+		assert_eq!(Balances::free_balance(10), 90);
+		assert_ok!(Identity::clear_identity(RuntimeOrigin::signed(10)));
+		assert_eq!(Balances::free_balance(10), 100);
+		assert_noop!(
+			Identity::clear_identity(RuntimeOrigin::signed(10)),
+			Error::<Test>::NotNamed
+		);
+	});
+}
+
+#[test]
+fn uninvited_judgement_should_work() {
+	new_test_ext().execute_with(|| {
+		assert_noop!(
+			Identity::provide_judgement(
+				RuntimeOrigin::signed(3),
+				0,
+				10,
+				Judgement::Reasonable,
+				H256::random()
+			),
+			Error::<Test>::InvalidIndex
+		);
+
+		assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 3));
+		assert_noop!(
+			Identity::provide_judgement(
+				RuntimeOrigin::signed(3),
+				0,
+				10,
+				Judgement::Reasonable,
+				H256::random()
+			),
+			Error::<Test>::InvalidTarget
+		);
+
+		assert_ok!(Identity::set_identity(
+			RuntimeOrigin::signed(10),
+			Box::new(ten())
+		));
+		assert_noop!(
+			Identity::provide_judgement(
+				RuntimeOrigin::signed(3),
+				0,
+				10,
+				Judgement::Reasonable,
+				H256::random()
+			),
+			Error::<Test>::JudgementForDifferentIdentity
+		);
+
+		let identity_hash = BlakeTwo256::hash_of(&ten());
+
+		assert_noop!(
+			Identity::provide_judgement(
+				RuntimeOrigin::signed(10),
+				0,
+				10,
+				Judgement::Reasonable,
+				identity_hash
+			),
+			Error::<Test>::InvalidIndex
+		);
+		assert_noop!(
+			Identity::provide_judgement(
+				RuntimeOrigin::signed(3),
+				0,
+				10,
+				Judgement::FeePaid(1),
+				identity_hash
+			),
+			Error::<Test>::InvalidJudgement
+		);
+
+		assert_ok!(Identity::provide_judgement(
+			RuntimeOrigin::signed(3),
+			0,
+			10,
+			Judgement::Reasonable,
+			identity_hash
+		));
+		assert_eq!(
+			Identity::identity(10).unwrap().judgements,
+			vec![(0, Judgement::Reasonable)]
+		);
+	});
+}
+
+#[test]
+fn clearing_judgement_should_work() {
+	new_test_ext().execute_with(|| {
+		assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 3));
+		assert_ok!(Identity::set_identity(
+			RuntimeOrigin::signed(10),
+			Box::new(ten())
+		));
+		assert_ok!(Identity::provide_judgement(
+			RuntimeOrigin::signed(3),
+			0,
+			10,
+			Judgement::Reasonable,
+			BlakeTwo256::hash_of(&ten())
+		));
+		assert_ok!(Identity::clear_identity(RuntimeOrigin::signed(10)));
+		assert_eq!(Identity::identity(10), None);
+	});
+}
+
+#[test]
+fn killing_slashing_should_work() {
+	new_test_ext().execute_with(|| {
+		assert_ok!(Identity::set_identity(
+			RuntimeOrigin::signed(10),
+			Box::new(ten())
+		));
+		assert_noop!(
+			Identity::kill_identity(RuntimeOrigin::signed(1), 10),
+			BadOrigin
+		);
+		assert_ok!(Identity::kill_identity(RuntimeOrigin::signed(2), 10));
+		assert_eq!(Identity::identity(10), None);
+		assert_eq!(Balances::free_balance(10), 90);
+		assert_noop!(
+			Identity::kill_identity(RuntimeOrigin::signed(2), 10),
+			Error::<Test>::NotNamed
+		);
+	});
+}
+
+#[test]
+fn setting_subaccounts_should_work() {
+	new_test_ext().execute_with(|| {
+		let mut subs = vec![(20, Data::Raw(vec![40; 1].try_into().unwrap()))];
+		assert_noop!(
+			Identity::set_subs(RuntimeOrigin::signed(10), subs.clone()),
+			Error::<Test>::NotFound
+		);
+
+		assert_ok!(Identity::set_identity(
+			RuntimeOrigin::signed(10),
+			Box::new(ten())
+		));
+		assert_ok!(Identity::set_subs(RuntimeOrigin::signed(10), subs.clone()));
+		assert_eq!(Balances::free_balance(10), 80);
+		assert_eq!(Identity::subs_of(10), (10, vec![20].try_into().unwrap()));
+		assert_eq!(
+			Identity::super_of(20),
+			Some((10, Data::Raw(vec![40; 1].try_into().unwrap())))
+		);
+
+		// push another item and re-set it.
+		subs.push((30, Data::Raw(vec![50; 1].try_into().unwrap())));
+		assert_ok!(Identity::set_subs(RuntimeOrigin::signed(10), subs.clone()));
+		assert_eq!(Balances::free_balance(10), 70);
+		assert_eq!(
+			Identity::subs_of(10),
+			(20, vec![20, 30].try_into().unwrap())
+		);
+		assert_eq!(
+			Identity::super_of(20),
+			Some((10, Data::Raw(vec![40; 1].try_into().unwrap())))
+		);
+		assert_eq!(
+			Identity::super_of(30),
+			Some((10, Data::Raw(vec![50; 1].try_into().unwrap())))
+		);
+
+		// switch out one of the items and re-set.
+		subs[0] = (40, Data::Raw(vec![60; 1].try_into().unwrap()));
+		assert_ok!(Identity::set_subs(RuntimeOrigin::signed(10), subs.clone()));
+		assert_eq!(Balances::free_balance(10), 70); // no change in the balance
+		assert_eq!(
+			Identity::subs_of(10),
+			(20, vec![40, 30].try_into().unwrap())
+		);
+		assert_eq!(Identity::super_of(20), None);
+		assert_eq!(
+			Identity::super_of(30),
+			Some((10, Data::Raw(vec![50; 1].try_into().unwrap())))
+		);
+		assert_eq!(
+			Identity::super_of(40),
+			Some((10, Data::Raw(vec![60; 1].try_into().unwrap())))
+		);
+
+		// clear
+		assert_ok!(Identity::set_subs(RuntimeOrigin::signed(10), vec![]));
+		assert_eq!(Balances::free_balance(10), 90);
+		assert_eq!(Identity::subs_of(10), (0, BoundedVec::default()));
+		assert_eq!(Identity::super_of(30), None);
+		assert_eq!(Identity::super_of(40), None);
+
+		subs.push((20, Data::Raw(vec![40; 1].try_into().unwrap())));
+		assert_noop!(
+			Identity::set_subs(RuntimeOrigin::signed(10), subs.clone()),
+			Error::<Test>::TooManySubAccounts
+		);
+	});
+}
+
+#[test]
+fn clearing_account_should_remove_subaccounts_and_refund() {
+	new_test_ext().execute_with(|| {
+		assert_ok!(Identity::set_identity(
+			RuntimeOrigin::signed(10),
+			Box::new(ten())
+		));
+		assert_ok!(Identity::set_subs(
+			RuntimeOrigin::signed(10),
+			vec![(20, Data::Raw(vec![40; 1].try_into().unwrap()))]
+		));
+		assert_ok!(Identity::clear_identity(RuntimeOrigin::signed(10)));
+		assert_eq!(Balances::free_balance(10), 100);
+		assert!(Identity::super_of(20).is_none());
+	});
+}
+
+#[test]
+fn killing_account_should_remove_subaccounts_and_not_refund() {
+	new_test_ext().execute_with(|| {
+		assert_ok!(Identity::set_identity(
+			RuntimeOrigin::signed(10),
+			Box::new(ten())
+		));
+		assert_ok!(Identity::set_subs(
+			RuntimeOrigin::signed(10),
+			vec![(20, Data::Raw(vec![40; 1].try_into().unwrap()))]
+		));
+		assert_ok!(Identity::kill_identity(RuntimeOrigin::signed(2), 10));
+		assert_eq!(Balances::free_balance(10), 80);
+		assert!(Identity::super_of(20).is_none());
+	});
+}
+
+#[test]
+fn cancelling_requested_judgement_should_work() {
+	new_test_ext().execute_with(|| {
+		assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 3));
+		assert_ok!(Identity::set_fee(RuntimeOrigin::signed(3), 0, 10));
+		assert_noop!(
+			Identity::cancel_request(RuntimeOrigin::signed(10), 0),
+			Error::<Test>::NoIdentity
+		);
+		assert_ok!(Identity::set_identity(
+			RuntimeOrigin::signed(10),
+			Box::new(ten())
+		));
+		assert_ok!(Identity::request_judgement(
+			RuntimeOrigin::signed(10),
+			0,
+			10
+		));
+		assert_ok!(Identity::cancel_request(RuntimeOrigin::signed(10), 0));
+		assert_eq!(Balances::free_balance(10), 90);
+		assert_noop!(
+			Identity::cancel_request(RuntimeOrigin::signed(10), 0),
+			Error::<Test>::NotFound
+		);
+
+		assert_ok!(Identity::provide_judgement(
+			RuntimeOrigin::signed(3),
+			0,
+			10,
+			Judgement::Reasonable,
+			BlakeTwo256::hash_of(&ten())
+		));
+		assert_noop!(
+			Identity::cancel_request(RuntimeOrigin::signed(10), 0),
+			Error::<Test>::JudgementGiven
+		);
+	});
+}
+
+#[test]
+fn requesting_judgement_should_work() {
+	new_test_ext().execute_with(|| {
+		assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 3));
+		assert_ok!(Identity::set_fee(RuntimeOrigin::signed(3), 0, 10));
+		assert_ok!(Identity::set_identity(
+			RuntimeOrigin::signed(10),
+			Box::new(ten())
+		));
+		assert_noop!(
+			Identity::request_judgement(RuntimeOrigin::signed(10), 0, 9),
+			Error::<Test>::FeeChanged
+		);
+		assert_ok!(Identity::request_judgement(
+			RuntimeOrigin::signed(10),
+			0,
+			10
+		));
+		// 10 for the judgement request, 10 for the identity.
+		assert_eq!(Balances::free_balance(10), 80);
+
+		// Re-requesting won't work as we already paid.
+		assert_noop!(
+			Identity::request_judgement(RuntimeOrigin::signed(10), 0, 10),
+			Error::<Test>::StickyJudgement
+		);
+		assert_ok!(Identity::provide_judgement(
+			RuntimeOrigin::signed(3),
+			0,
+			10,
+			Judgement::Erroneous,
+			BlakeTwo256::hash_of(&ten())
+		));
+		// Registrar got their payment now.
+		assert_eq!(Balances::free_balance(3), 20);
+
+		// Re-requesting still won't work as it's erroneous.
+		assert_noop!(
+			Identity::request_judgement(RuntimeOrigin::signed(10), 0, 10),
+			Error::<Test>::StickyJudgement
+		);
+
+		// Requesting from a second registrar still works.
+		assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 4));
+		assert_ok!(Identity::request_judgement(
+			RuntimeOrigin::signed(10),
+			1,
+			10
+		));
+
+		// Re-requesting after the judgement has been reduced works.
+		assert_ok!(Identity::provide_judgement(
+			RuntimeOrigin::signed(3),
+			0,
+			10,
+			Judgement::OutOfDate,
+			BlakeTwo256::hash_of(&ten())
+		));
+		assert_ok!(Identity::request_judgement(
+			RuntimeOrigin::signed(10),
+			0,
+			10
+		));
+	});
+}
+
+#[test]
+fn provide_judgement_should_return_judgement_payment_failed_error() {
+	new_test_ext().execute_with(|| {
+		assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 3));
+		assert_ok!(Identity::set_fee(RuntimeOrigin::signed(3), 0, 10));
+		assert_ok!(Identity::set_identity(
+			RuntimeOrigin::signed(10),
+			Box::new(ten())
+		));
+		assert_ok!(Identity::request_judgement(
+			RuntimeOrigin::signed(10),
+			0,
+			10
+		));
+		// 10 for the judgement request, 10 for the identity.
+		assert_eq!(Balances::free_balance(10), 80);
+
+		// This forces judgement payment failed error
+		Balances::make_free_balance_be(&3, 0);
+		assert_noop!(
+			Identity::provide_judgement(
+				RuntimeOrigin::signed(3),
+				0,
+				10,
+				Judgement::Erroneous,
+				BlakeTwo256::hash_of(&ten())
+			),
+			Error::<Test>::JudgementPaymentFailed
+		);
+	});
+}
+
+#[test]
+fn field_deposit_should_work() {
+	new_test_ext().execute_with(|| {
+		assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 3));
+		assert_ok!(Identity::set_fee(RuntimeOrigin::signed(3), 0, 10));
+		assert_ok!(Identity::set_identity(
+			RuntimeOrigin::signed(10),
+			Box::new(IdentityInfo {
+				additional: vec![
+					(
+						Data::Raw(b"number".to_vec().try_into().unwrap()),
+						Data::Raw(10u32.encode().try_into().unwrap())
+					),
+					(
+						Data::Raw(b"text".to_vec().try_into().unwrap()),
+						Data::Raw(b"10".to_vec().try_into().unwrap())
+					),
+				]
+				.try_into()
+				.unwrap(),
+				..Default::default()
+			})
+		));
+		assert_eq!(Balances::free_balance(10), 70);
+	});
+}
+
+#[test]
+fn setting_account_id_should_work() {
+	new_test_ext().execute_with(|| {
+		assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 3));
+		// account 4 cannot change the first registrar's identity since it's owned by 3.
+		assert_noop!(
+			Identity::set_account_id(RuntimeOrigin::signed(4), 0, 3),
+			Error::<Test>::InvalidIndex
+		);
+		// account 3 can, because that's the registrar's current account.
+		assert_ok!(Identity::set_account_id(RuntimeOrigin::signed(3), 0, 4));
+		// account 4 can now, because that's their new ID.
+		assert_ok!(Identity::set_account_id(RuntimeOrigin::signed(4), 0, 3));
+	});
+}
+
+#[test]
+fn test_has_identity() {
+	new_test_ext().execute_with(|| {
+		assert_ok!(Identity::set_identity(
+			RuntimeOrigin::signed(10),
+			Box::new(ten())
+		));
+		assert!(Identity::has_identity(&10, IdentityField::Display as u64));
+		assert!(Identity::has_identity(&10, IdentityField::Legal as u64));
+		assert!(Identity::has_identity(
+			&10,
+			IdentityField::Display as u64 | IdentityField::Legal as u64
+		));
+		assert!(!Identity::has_identity(
+			&10,
+			IdentityField::Display as u64 | IdentityField::Legal as u64 | IdentityField::Web as u64
+		));
+	});
+}
addedpallets/identity/src/types.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/identity/src/types.rs
@@ -0,0 +1,540 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+// Original license:
+// 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.
+
+use super::*;
+use codec::{Decode, Encode, MaxEncodedLen};
+use enumflags2::{bitflags, BitFlags};
+use frame_support::{
+	traits::{ConstU32, Get},
+	BoundedVec, CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound,
+};
+use scale_info::{
+	build::{Fields, Variants},
+	meta_type, Path, Type, TypeInfo, TypeParameter,
+};
+use sp_runtime::{traits::Zero, RuntimeDebug};
+use sp_std::{fmt::Debug, iter::once, ops::Add, prelude::*};
+
+/// Either underlying data blob if it is at most 32 bytes, or a hash of it. If the data is greater
+/// than 32-bytes then it will be truncated when encoding.
+///
+/// Can also be `None`.
+#[derive(Clone, Eq, PartialEq, RuntimeDebug, MaxEncodedLen)]
+pub enum Data {
+	/// No data here.
+	None,
+	/// The data is stored directly.
+	Raw(BoundedVec<u8, ConstU32<32>>),
+	/// Only the Blake2 hash of the data is stored. The preimage of the hash may be retrieved
+	/// through some hash-lookup service.
+	BlakeTwo256([u8; 32]),
+	/// Only the SHA2-256 hash of the data is stored. The preimage of the hash may be retrieved
+	/// through some hash-lookup service.
+	Sha256([u8; 32]),
+	/// Only the Keccak-256 hash of the data is stored. The preimage of the hash may be retrieved
+	/// through some hash-lookup service.
+	Keccak256([u8; 32]),
+	/// Only the SHA3-256 hash of the data is stored. The preimage of the hash may be retrieved
+	/// through some hash-lookup service.
+	ShaThree256([u8; 32]),
+}
+
+impl Data {
+	pub fn is_none(&self) -> bool {
+		self == &Data::None
+	}
+}
+
+impl Decode for Data {
+	fn decode<I: codec::Input>(input: &mut I) -> sp_std::result::Result<Self, codec::Error> {
+		let b = input.read_byte()?;
+		Ok(match b {
+			0 => Data::None,
+			n @ 1..=33 => {
+				let mut r: BoundedVec<_, _> = vec![0u8; n as usize - 1]
+					.try_into()
+					.expect("bound checked in match arm condition; qed");
+				input.read(&mut r[..])?;
+				Data::Raw(r)
+			}
+			34 => Data::BlakeTwo256(<[u8; 32]>::decode(input)?),
+			35 => Data::Sha256(<[u8; 32]>::decode(input)?),
+			36 => Data::Keccak256(<[u8; 32]>::decode(input)?),
+			37 => Data::ShaThree256(<[u8; 32]>::decode(input)?),
+			_ => return Err(codec::Error::from("invalid leading byte")),
+		})
+	}
+}
+
+impl Encode for Data {
+	fn encode(&self) -> Vec<u8> {
+		match self {
+			Data::None => vec![0u8; 1],
+			Data::Raw(ref x) => {
+				let l = x.len().min(32);
+				let mut r = vec![l as u8 + 1; l + 1];
+				r[1..].copy_from_slice(&x[..l as usize]);
+				r
+			}
+			Data::BlakeTwo256(ref h) => once(34u8).chain(h.iter().cloned()).collect(),
+			Data::Sha256(ref h) => once(35u8).chain(h.iter().cloned()).collect(),
+			Data::Keccak256(ref h) => once(36u8).chain(h.iter().cloned()).collect(),
+			Data::ShaThree256(ref h) => once(37u8).chain(h.iter().cloned()).collect(),
+		}
+	}
+}
+impl codec::EncodeLike for Data {}
+
+/// Add a Raw variant with the given index and a fixed sized byte array
+macro_rules! data_raw_variants {
+    ($variants:ident, $(($index:literal, $size:literal)),* ) => {
+		$variants
+		$(
+			.variant(concat!("Raw", stringify!($size)), |v| v
+				.index($index)
+				.fields(Fields::unnamed().field(|f| f.ty::<[u8; $size]>()))
+			)
+		)*
+    }
+}
+
+impl TypeInfo for Data {
+	type Identity = Self;
+
+	fn type_info() -> Type {
+		let variants = Variants::new().variant("None", |v| v.index(0));
+
+		// create a variant for all sizes of Raw data from 0-32
+		let variants = data_raw_variants!(
+			variants,
+			(1, 0),
+			(2, 1),
+			(3, 2),
+			(4, 3),
+			(5, 4),
+			(6, 5),
+			(7, 6),
+			(8, 7),
+			(9, 8),
+			(10, 9),
+			(11, 10),
+			(12, 11),
+			(13, 12),
+			(14, 13),
+			(15, 14),
+			(16, 15),
+			(17, 16),
+			(18, 17),
+			(19, 18),
+			(20, 19),
+			(21, 20),
+			(22, 21),
+			(23, 22),
+			(24, 23),
+			(25, 24),
+			(26, 25),
+			(27, 26),
+			(28, 27),
+			(29, 28),
+			(30, 29),
+			(31, 30),
+			(32, 31),
+			(33, 32)
+		);
+
+		let variants = variants
+			.variant("BlakeTwo256", |v| {
+				v.index(34)
+					.fields(Fields::unnamed().field(|f| f.ty::<[u8; 32]>()))
+			})
+			.variant("Sha256", |v| {
+				v.index(35)
+					.fields(Fields::unnamed().field(|f| f.ty::<[u8; 32]>()))
+			})
+			.variant("Keccak256", |v| {
+				v.index(36)
+					.fields(Fields::unnamed().field(|f| f.ty::<[u8; 32]>()))
+			})
+			.variant("ShaThree256", |v| {
+				v.index(37)
+					.fields(Fields::unnamed().field(|f| f.ty::<[u8; 32]>()))
+			});
+
+		Type::builder()
+			.path(Path::new("Data", module_path!()))
+			.variant(variants)
+	}
+}
+
+impl Default for Data {
+	fn default() -> Self {
+		Self::None
+	}
+}
+
+/// An identifier for a single name registrar/identity verification service.
+pub type RegistrarIndex = u32;
+
+/// An attestation of a registrar over how accurate some `IdentityInfo` is in describing an account.
+///
+/// NOTE: Registrars may pay little attention to some fields. Registrars may want to make clear
+/// which fields their attestation is relevant for by off-chain means.
+#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
+pub enum Judgement<Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq>
+{
+	/// The default value; no opinion is held.
+	Unknown,
+	/// No judgement is yet in place, but a deposit is reserved as payment for providing one.
+	FeePaid(Balance),
+	/// The data appears to be reasonably acceptable in terms of its accuracy, however no in depth
+	/// checks (such as in-person meetings or formal KYC) have been conducted.
+	Reasonable,
+	/// The target is known directly by the registrar and the registrar can fully attest to the
+	/// the data's accuracy.
+	KnownGood,
+	/// The data was once good but is currently out of date. There is no malicious intent in the
+	/// inaccuracy. This judgement can be removed through updating the data.
+	OutOfDate,
+	/// The data is imprecise or of sufficiently low-quality to be problematic. It is not
+	/// indicative of malicious intent. This judgement can be removed through updating the data.
+	LowQuality,
+	/// The data is erroneous. This may be indicative of malicious intent. This cannot be removed
+	/// except by the registrar.
+	Erroneous,
+}
+
+impl<Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq>
+	Judgement<Balance>
+{
+	/// Returns `true` if this judgement is indicative of a deposit being currently held. This means
+	/// it should not be cleared or replaced except by an operation which utilizes the deposit.
+	pub(crate) fn has_deposit(&self) -> bool {
+		matches!(self, Judgement::FeePaid(_))
+	}
+
+	/// Returns `true` if this judgement is one that should not be generally be replaced outside
+	/// of specialized handlers. Examples include "malicious" judgements and deposit-holding
+	/// judgements.
+	pub(crate) fn is_sticky(&self) -> bool {
+		matches!(self, Judgement::FeePaid(_) | Judgement::Erroneous)
+	}
+}
+
+/// The fields that we use to identify the owner of an account with. Each corresponds to a field
+/// in the `IdentityInfo` struct.
+#[bitflags]
+#[repr(u64)]
+#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)]
+pub enum IdentityField {
+	Display = 0b0000000000000000000000000000000000000000000000000000000000000001,
+	Legal = 0b0000000000000000000000000000000000000000000000000000000000000010,
+	Web = 0b0000000000000000000000000000000000000000000000000000000000000100,
+	Riot = 0b0000000000000000000000000000000000000000000000000000000000001000,
+	Email = 0b0000000000000000000000000000000000000000000000000000000000010000,
+	PgpFingerprint = 0b0000000000000000000000000000000000000000000000000000000000100000,
+	Image = 0b0000000000000000000000000000000000000000000000000000000001000000,
+	Twitter = 0b0000000000000000000000000000000000000000000000000000000010000000,
+}
+
+/// Wrapper type for `BitFlags<IdentityField>` that implements `Codec`.
+#[derive(Clone, Copy, PartialEq, Default, RuntimeDebug)]
+pub struct IdentityFields(pub BitFlags<IdentityField>);
+
+impl MaxEncodedLen for IdentityFields {
+	fn max_encoded_len() -> usize {
+		u64::max_encoded_len()
+	}
+}
+
+impl Eq for IdentityFields {}
+impl Encode for IdentityFields {
+	fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
+		self.0.bits().using_encoded(f)
+	}
+}
+impl Decode for IdentityFields {
+	fn decode<I: codec::Input>(input: &mut I) -> sp_std::result::Result<Self, codec::Error> {
+		let field = u64::decode(input)?;
+		Ok(Self(
+			<BitFlags<IdentityField>>::from_bits(field as u64).map_err(|_| "invalid value")?,
+		))
+	}
+}
+impl TypeInfo for IdentityFields {
+	type Identity = Self;
+
+	fn type_info() -> Type {
+		Type::builder()
+			.path(Path::new("BitFlags", module_path!()))
+			.type_params(vec![TypeParameter::new(
+				"T",
+				Some(meta_type::<IdentityField>()),
+			)])
+			.composite(Fields::unnamed().field(|f| f.ty::<u64>().type_name("IdentityField")))
+	}
+}
+
+/// Information concerning the identity of the controller of an account.
+///
+/// NOTE: This should be stored at the end of the storage item to facilitate the addition of extra
+/// fields in a backwards compatible way through a specialized `Decode` impl.
+#[derive(
+	CloneNoBound, Encode, Decode, Eq, MaxEncodedLen, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo,
+)]
+#[codec(mel_bound())]
+#[cfg_attr(test, derive(frame_support::DefaultNoBound))]
+#[scale_info(skip_type_params(FieldLimit))]
+pub struct IdentityInfo<FieldLimit: Get<u32>> {
+	/// Additional fields of the identity that are not catered for with the struct's explicit
+	/// fields.
+	pub additional: BoundedVec<(Data, Data), FieldLimit>,
+
+	/// A reasonable display name for the controller of the account. This should be whatever it is
+	/// that it is typically known as and should not be confusable with other entities, given
+	/// reasonable context.
+	///
+	/// Stored as UTF-8.
+	pub display: Data,
+
+	/// The full legal name in the local jurisdiction of the entity. This might be a bit
+	/// long-winded.
+	///
+	/// Stored as UTF-8.
+	pub legal: Data,
+
+	/// A representative website held by the controller of the account.
+	///
+	/// NOTE: `https://` is automatically prepended.
+	///
+	/// Stored as UTF-8.
+	pub web: Data,
+
+	/// The Riot/Matrix handle held by the controller of the account.
+	///
+	/// Stored as UTF-8.
+	pub riot: Data,
+
+	/// The email address of the controller of the account.
+	///
+	/// Stored as UTF-8.
+	pub email: Data,
+
+	/// The PGP/GPG public key of the controller of the account.
+	pub pgp_fingerprint: Option<[u8; 20]>,
+
+	/// A graphic image representing the controller of the account. Should be a company,
+	/// organization or project logo or a headshot in the case of a human.
+	pub image: Data,
+
+	/// The Twitter identity. The leading `@` character may be elided.
+	pub twitter: Data,
+}
+
+impl<FieldLimit: Get<u32>> IdentityInfo<FieldLimit> {
+	pub(crate) fn fields(&self) -> IdentityFields {
+		let mut res = <BitFlags<IdentityField>>::empty();
+		if !self.display.is_none() {
+			res.insert(IdentityField::Display);
+		}
+		if !self.legal.is_none() {
+			res.insert(IdentityField::Legal);
+		}
+		if !self.web.is_none() {
+			res.insert(IdentityField::Web);
+		}
+		if !self.riot.is_none() {
+			res.insert(IdentityField::Riot);
+		}
+		if !self.email.is_none() {
+			res.insert(IdentityField::Email);
+		}
+		if self.pgp_fingerprint.is_some() {
+			res.insert(IdentityField::PgpFingerprint);
+		}
+		if !self.image.is_none() {
+			res.insert(IdentityField::Image);
+		}
+		if !self.twitter.is_none() {
+			res.insert(IdentityField::Twitter);
+		}
+		IdentityFields(res)
+	}
+}
+
+/// Information concerning the identity of the controller of an account.
+///
+/// NOTE: This is stored separately primarily to facilitate the addition of extra fields in a
+/// backwards compatible way through a specialized `Decode` impl.
+#[derive(
+	CloneNoBound, Encode, Eq, MaxEncodedLen, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo,
+)]
+#[codec(mel_bound())]
+#[scale_info(skip_type_params(MaxJudgements, MaxAdditionalFields))]
+pub struct Registration<
+	Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq,
+	MaxJudgements: Get<u32>,
+	MaxAdditionalFields: Get<u32>,
+> {
+	/// Judgements from the registrars on this identity. Stored ordered by `RegistrarIndex`. There
+	/// may be only a single judgement from each registrar.
+	pub judgements: BoundedVec<(RegistrarIndex, Judgement<Balance>), MaxJudgements>,
+
+	/// Amount held on deposit for this information.
+	pub deposit: Balance,
+
+	/// Information on the identity.
+	pub info: IdentityInfo<MaxAdditionalFields>,
+}
+
+impl<
+		Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq + Zero + Add,
+		MaxJudgements: Get<u32>,
+		MaxAdditionalFields: Get<u32>,
+	> Registration<Balance, MaxJudgements, MaxAdditionalFields>
+{
+	pub(crate) fn total_deposit(&self) -> Balance {
+		self.deposit
+			+ self
+				.judgements
+				.iter()
+				.map(|(_, ref j)| {
+					if let Judgement::FeePaid(fee) = j {
+						*fee
+					} else {
+						Zero::zero()
+					}
+				})
+				.fold(Zero::zero(), |a, i| a + i)
+	}
+}
+
+impl<
+		Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq,
+		MaxJudgements: Get<u32>,
+		MaxAdditionalFields: Get<u32>,
+	> Decode for Registration<Balance, MaxJudgements, MaxAdditionalFields>
+{
+	fn decode<I: codec::Input>(input: &mut I) -> sp_std::result::Result<Self, codec::Error> {
+		let (judgements, deposit, info) = Decode::decode(&mut AppendZerosInput::new(input))?;
+		Ok(Self {
+			judgements,
+			deposit,
+			info,
+		})
+	}
+}
+
+/// Information concerning a registrar.
+#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
+pub struct RegistrarInfo<
+	Balance: Encode + Decode + Clone + Debug + Eq + PartialEq,
+	AccountId: Encode + Decode + Clone + Debug + Eq + PartialEq,
+> {
+	/// The account of the registrar.
+	pub account: AccountId,
+
+	/// Amount required to be given to the registrar for them to provide judgement.
+	pub fee: Balance,
+
+	/// Relevant fields for this registrar. Registrar judgements are limited to attestations on
+	/// these fields.
+	pub fields: IdentityFields,
+}
+
+#[cfg(test)]
+mod tests {
+	use super::*;
+
+	#[test]
+	fn manual_data_type_info() {
+		let mut registry = scale_info::Registry::new();
+		let type_id = registry.register_type(&scale_info::meta_type::<Data>());
+		let registry: scale_info::PortableRegistry = registry.into();
+		let type_info = registry.resolve(type_id.id()).unwrap();
+
+		let check_type_info = |data: &Data| {
+			let variant_name = match data {
+				Data::None => "None".to_string(),
+				Data::BlakeTwo256(_) => "BlakeTwo256".to_string(),
+				Data::Sha256(_) => "Sha256".to_string(),
+				Data::Keccak256(_) => "Keccak256".to_string(),
+				Data::ShaThree256(_) => "ShaThree256".to_string(),
+				Data::Raw(bytes) => format!("Raw{}", bytes.len()),
+			};
+			if let scale_info::TypeDef::Variant(variant) = type_info.type_def() {
+				let variant = variant
+					.variants()
+					.iter()
+					.find(|v| v.name() == &variant_name)
+					.expect(&format!("Expected to find variant {}", variant_name));
+
+				let field_arr_len = variant
+					.fields()
+					.first()
+					.and_then(|f| registry.resolve(f.ty().id()))
+					.map(|ty| {
+						if let scale_info::TypeDef::Array(arr) = ty.type_def() {
+							arr.len()
+						} else {
+							panic!("Should be an array type")
+						}
+					})
+					.unwrap_or(0);
+
+				let encoded = data.encode();
+				assert_eq!(encoded[0], variant.index());
+				assert_eq!(encoded.len() as u32 - 1, field_arr_len);
+			} else {
+				panic!("Should be a variant type")
+			};
+		};
+
+		let mut data = vec![
+			Data::None,
+			Data::BlakeTwo256(Default::default()),
+			Data::Sha256(Default::default()),
+			Data::Keccak256(Default::default()),
+			Data::ShaThree256(Default::default()),
+		];
+
+		// A Raw instance for all possible sizes of the Raw data
+		for n in 0..32 {
+			data.push(Data::Raw(vec![0u8; n as usize].try_into().unwrap()))
+		}
+
+		for d in data.iter() {
+			check_type_info(d);
+		}
+	}
+}
addedpallets/identity/src/weights.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/identity/src/weights.rs
@@ -0,0 +1,558 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+// Original license:
+// This file is part of Substrate.
+
+// Copyright (C) 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.
+
+//! Autogenerated weights for pallet_identity
+//!
+//! 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: `[]`
+//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
+//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
+
+// Executed Command:
+// ./target/production/substrate
+// benchmark
+// pallet
+// --chain=dev
+// --steps=50
+// --repeat=20
+// --pallet=pallet_identity
+// --extrinsic=*
+// --execution=wasm
+// --wasm-execution=compiled
+// --heap-pages=4096
+// --output=./frame/identity/src/weights.rs
+// --header=./HEADER-APACHE2
+// --template=./.maintain/frame-weight-template.hbs
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+
+use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
+use sp_std::marker::PhantomData;
+
+/// Weight functions needed for pallet_identity.
+pub trait WeightInfo {
+	fn add_registrar(r: u32, ) -> Weight;
+	fn set_identity(r: u32, x: u32, ) -> Weight;
+	fn set_subs_new(s: u32, ) -> Weight;
+	fn set_subs_old(p: u32, ) -> Weight;
+	fn clear_identity(r: u32, s: u32, x: u32, ) -> Weight;
+	fn request_judgement(r: u32, x: u32, ) -> Weight;
+	fn cancel_request(r: u32, x: u32, ) -> Weight;
+	fn set_fee(r: u32, ) -> Weight;
+	fn set_account_id(r: u32, ) -> Weight;
+	fn set_fields(r: u32, ) -> Weight;
+	fn provide_judgement(r: u32, x: u32, ) -> Weight;
+	fn kill_identity(r: u32, s: u32, x: u32, ) -> Weight;
+	fn force_insert_identities(x: u32, n: u32, ) -> Weight;
+	fn force_remove_identities(x: u32, n: u32, ) -> Weight;
+	fn add_sub(s: u32, ) -> Weight;
+	fn rename_sub(s: u32, ) -> Weight;
+	fn remove_sub(s: u32, ) -> Weight;
+	fn quit_sub(s: u32, ) -> Weight;
+}
+
+/// Weights for pallet_identity using the Substrate node and recommended hardware.
+pub struct SubstrateWeight<T>(PhantomData<T>);
+impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
+	// Storage: Identity Registrars (r:1 w:1)
+	/// The range of component `r` is `[1, 19]`.
+	fn add_registrar(r: u32, ) -> Weight {
+		// Minimum execution time: 20_269 nanoseconds.
+		Weight::from_ref_time(21_910_543 as u64)
+			// Standard Error: 4_604
+			.saturating_add(Weight::from_ref_time(223_104 as u64).saturating_mul(r as u64))
+			.saturating_add(T::DbWeight::get().reads(1 as u64))
+			.saturating_add(T::DbWeight::get().writes(1 as u64))
+	}
+	// Storage: Identity IdentityOf (r:1 w:1)
+	/// The range of component `r` is `[1, 20]`.
+	/// The range of component `x` is `[0, 100]`.
+	fn set_identity(r: u32, x: u32, ) -> Weight {
+		// Minimum execution time: 41_872 nanoseconds.
+		Weight::from_ref_time(40_230_216 as u64)
+			// Standard Error: 2_342
+			.saturating_add(Weight::from_ref_time(145_168 as u64).saturating_mul(r as u64))
+			// Standard Error: 457
+			.saturating_add(Weight::from_ref_time(291_732 as u64).saturating_mul(x as u64))
+			.saturating_add(T::DbWeight::get().reads(1 as u64))
+			.saturating_add(T::DbWeight::get().writes(1 as u64))
+	}
+	// Storage: Identity IdentityOf (r:1 w:0)
+	// Storage: Identity SubsOf (r:1 w:1)
+	// Storage: Identity SuperOf (r:2 w:2)
+	/// The range of component `s` is `[0, 100]`.
+	fn set_subs_new(s: u32, ) -> Weight {
+		// Minimum execution time: 12_024 nanoseconds.
+		Weight::from_ref_time(32_550_819 as u64)
+			// Standard Error: 5_057
+			.saturating_add(Weight::from_ref_time(2_521_245 as u64).saturating_mul(s as u64))
+			.saturating_add(T::DbWeight::get().reads(2 as u64))
+			.saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(s as u64)))
+			.saturating_add(T::DbWeight::get().writes(1 as u64))
+			.saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(s as u64)))
+	}
+	// Storage: Identity IdentityOf (r:1 w:0)
+	// Storage: Identity SubsOf (r:1 w:1)
+	// Storage: Identity SuperOf (r:0 w:2)
+	/// The range of component `p` is `[0, 100]`.
+	fn set_subs_old(p: u32, ) -> Weight {
+		// Minimum execution time: 12_232 nanoseconds.
+		Weight::from_ref_time(34_009_761 as u64)
+			// Standard Error: 5_047
+			.saturating_add(Weight::from_ref_time(1_113_100 as u64).saturating_mul(p as u64))
+			.saturating_add(T::DbWeight::get().reads(2 as u64))
+			.saturating_add(T::DbWeight::get().writes(1 as u64))
+			.saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(p as u64)))
+	}
+	// Storage: Identity SubsOf (r:1 w:1)
+	// Storage: Identity IdentityOf (r:1 w:1)
+	// Storage: Identity SuperOf (r:0 w:100)
+	/// The range of component `r` is `[1, 20]`.
+	/// The range of component `s` is `[0, 100]`.
+	/// The range of component `x` is `[0, 100]`.
+	fn clear_identity(r: u32, s: u32, x: u32, ) -> Weight {
+		// Minimum execution time: 57_144 nanoseconds.
+		Weight::from_ref_time(41_559_247 as u64)
+			// Standard Error: 9_996
+			.saturating_add(Weight::from_ref_time(146_770 as u64).saturating_mul(r as u64))
+			// Standard Error: 1_952
+			.saturating_add(Weight::from_ref_time(1_086_673 as u64).saturating_mul(s as u64))
+			// Standard Error: 1_952
+			.saturating_add(Weight::from_ref_time(162_481 as u64).saturating_mul(x as u64))
+			.saturating_add(T::DbWeight::get().reads(2 as u64))
+			.saturating_add(T::DbWeight::get().writes(2 as u64))
+			.saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(s as u64)))
+	}
+	// Storage: Identity Registrars (r:1 w:0)
+	// Storage: Identity IdentityOf (r:1 w:1)
+	/// The range of component `r` is `[1, 20]`.
+	/// The range of component `x` is `[0, 100]`.
+	fn request_judgement(r: u32, x: u32, ) -> Weight {
+		// Minimum execution time: 44_726 nanoseconds.
+		Weight::from_ref_time(41_637_308 as u64)
+			// Standard Error: 1_907
+			.saturating_add(Weight::from_ref_time(219_078 as u64).saturating_mul(r as u64))
+			// Standard Error: 372
+			.saturating_add(Weight::from_ref_time(309_888 as u64).saturating_mul(x as u64))
+			.saturating_add(T::DbWeight::get().reads(2 as u64))
+			.saturating_add(T::DbWeight::get().writes(1 as u64))
+	}
+	// Storage: Identity IdentityOf (r:1 w:1)
+	/// The range of component `r` is `[1, 20]`.
+	/// The range of component `x` is `[0, 100]`.
+	fn cancel_request(r: u32, x: u32, ) -> Weight {
+		// Minimum execution time: 39_719 nanoseconds.
+		Weight::from_ref_time(38_008_751 as u64)
+			// Standard Error: 2_394
+			.saturating_add(Weight::from_ref_time(181_870 as u64).saturating_mul(r as u64))
+			// Standard Error: 467
+			.saturating_add(Weight::from_ref_time(314_990 as u64).saturating_mul(x as u64))
+			.saturating_add(T::DbWeight::get().reads(1 as u64))
+			.saturating_add(T::DbWeight::get().writes(1 as u64))
+	}
+	// Storage: Identity Registrars (r:1 w:1)
+	/// The range of component `r` is `[1, 19]`.
+	fn set_fee(r: u32, ) -> Weight {
+		// Minimum execution time: 10_634 nanoseconds.
+		Weight::from_ref_time(11_383_704 as u64)
+			// Standard Error: 2_250
+			.saturating_add(Weight::from_ref_time(193_094 as u64).saturating_mul(r as u64))
+			.saturating_add(T::DbWeight::get().reads(1 as u64))
+			.saturating_add(T::DbWeight::get().writes(1 as u64))
+	}
+	// Storage: Identity Registrars (r:1 w:1)
+	/// The range of component `r` is `[1, 19]`.
+	fn set_account_id(r: u32, ) -> Weight {
+		// Minimum execution time: 10_840 nanoseconds.
+		Weight::from_ref_time(11_638_740 as u64)
+			// Standard Error: 1_985
+			.saturating_add(Weight::from_ref_time(193_016 as u64).saturating_mul(r as u64))
+			.saturating_add(T::DbWeight::get().reads(1 as u64))
+			.saturating_add(T::DbWeight::get().writes(1 as u64))
+	}
+	// Storage: Identity Registrars (r:1 w:1)
+	/// The range of component `r` is `[1, 19]`.
+	fn set_fields(r: u32, ) -> Weight {
+		// Minimum execution time: 10_748 nanoseconds.
+		Weight::from_ref_time(11_346_901 as u64)
+			// Standard Error: 2_132
+			.saturating_add(Weight::from_ref_time(196_630 as u64).saturating_mul(r as u64))
+			.saturating_add(T::DbWeight::get().reads(1 as u64))
+			.saturating_add(T::DbWeight::get().writes(1 as u64))
+	}
+	// Storage: Identity Registrars (r:1 w:0)
+	// Storage: Identity IdentityOf (r:1 w:1)
+	/// The range of component `r` is `[1, 19]`.
+	/// The range of component `x` is `[0, 100]`.
+	fn provide_judgement(r: u32, x: u32, ) -> Weight {
+		// Minimum execution time: 33_682 nanoseconds.
+		Weight::from_ref_time(31_336_603 as u64)
+			// Standard Error: 3_056
+			.saturating_add(Weight::from_ref_time(200_403 as u64).saturating_mul(r as u64))
+			// Standard Error: 565
+			.saturating_add(Weight::from_ref_time(525_142 as u64).saturating_mul(x as u64))
+			.saturating_add(T::DbWeight::get().reads(2 as u64))
+			.saturating_add(T::DbWeight::get().writes(1 as u64))
+	}
+	// Storage: Identity SubsOf (r:1 w:1)
+	// Storage: Identity IdentityOf (r:1 w:1)
+	// Storage: System Account (r:1 w:1)
+	// Storage: Identity SuperOf (r:0 w:100)
+	/// The range of component `r` is `[1, 20]`.
+	/// The range of component `s` is `[0, 100]`.
+	/// The range of component `x` is `[0, 100]`.
+	fn kill_identity(r: u32, s: u32, x: u32, ) -> Weight {
+		// Minimum execution time: 68_794 nanoseconds.
+		Weight::from_ref_time(52_114_486 as u64)
+			// Standard Error: 4_808
+			.saturating_add(Weight::from_ref_time(153_462 as u64).saturating_mul(r as u64))
+			// Standard Error: 939
+			.saturating_add(Weight::from_ref_time(1_084_612 as u64).saturating_mul(s as u64))
+			// Standard Error: 939
+			.saturating_add(Weight::from_ref_time(170_112 as u64).saturating_mul(x as u64))
+			.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(s as u64)))
+	}
+	// Storage: Identity IdentityOf (r:1 w:1)
+	/// The range of component `x` is `[0, 100]`.
+	/// The range of component `n` is `[0, 600]`.
+	fn force_insert_identities(x: u32, n: u32) -> Weight {
+		// Minimum execution time: 41_872 nanoseconds.
+		Weight::from_ref_time(40_230_216 as u64)
+			// Standard Error: 2_342
+			.saturating_add(Weight::from_ref_time(145_168 as u64))
+			// Standard Error: 457
+			.saturating_add(Weight::from_ref_time(291_732 as u64).saturating_mul(x as u64))
+			.saturating_add(T::DbWeight::get().reads(1 as u64))
+			.saturating_add(T::DbWeight::get().writes(1 as u64).saturating_mul(n as u64))
+	}
+	// Storage: Identity IdentityOf (r:1 w:1)
+	/// The range of component `x` is `[0, 100]`.
+	/// The range of component `n` is `[0, 600]`.
+	fn force_remove_identities(x: u32, n: u32) -> Weight {
+		// Minimum execution time: 41_872 nanoseconds.
+		Weight::from_ref_time(40_230_216 as u64)
+			// Standard Error: 2_342
+			.saturating_add(Weight::from_ref_time(145_168 as u64))
+			// Standard Error: 457
+			.saturating_add(Weight::from_ref_time(291_732 as u64).saturating_mul(x as u64))
+			.saturating_add(T::DbWeight::get().reads(1 as u64))
+			.saturating_add(T::DbWeight::get().writes(1 as u64).saturating_mul(n as u64))
+	}
+	// Storage: Identity IdentityOf (r:1 w:0)
+	// Storage: Identity SuperOf (r:1 w:1)
+	// Storage: Identity SubsOf (r:1 w:1)
+	/// The range of component `s` is `[0, 99]`.
+	fn add_sub(s: u32, ) -> Weight {
+		// Minimum execution time: 37_914 nanoseconds.
+		Weight::from_ref_time(43_488_083 as u64)
+			// Standard Error: 1_631
+			.saturating_add(Weight::from_ref_time(118_845 as u64).saturating_mul(s as u64))
+			.saturating_add(T::DbWeight::get().reads(3 as u64))
+			.saturating_add(T::DbWeight::get().writes(2 as u64))
+	}
+	// Storage: Identity IdentityOf (r:1 w:0)
+	// Storage: Identity SuperOf (r:1 w:1)
+	/// The range of component `s` is `[1, 100]`.
+	fn rename_sub(s: u32, ) -> Weight {
+		// Minimum execution time: 16_124 nanoseconds.
+		Weight::from_ref_time(18_580_462 as u64)
+			// Standard Error: 688
+			.saturating_add(Weight::from_ref_time(67_220 as u64).saturating_mul(s as u64))
+			.saturating_add(T::DbWeight::get().reads(2 as u64))
+			.saturating_add(T::DbWeight::get().writes(1 as u64))
+	}
+	// Storage: Identity IdentityOf (r:1 w:0)
+	// Storage: Identity SuperOf (r:1 w:1)
+	// Storage: Identity SubsOf (r:1 w:1)
+	/// The range of component `s` is `[1, 100]`.
+	fn remove_sub(s: u32, ) -> Weight {
+		// Minimum execution time: 41_517 nanoseconds.
+		Weight::from_ref_time(45_123_530 as u64)
+			// Standard Error: 1_530
+			.saturating_add(Weight::from_ref_time(105_429 as u64).saturating_mul(s as u64))
+			.saturating_add(T::DbWeight::get().reads(3 as u64))
+			.saturating_add(T::DbWeight::get().writes(2 as u64))
+	}
+	// Storage: Identity SuperOf (r:1 w:1)
+	// Storage: Identity SubsOf (r:1 w:1)
+	/// The range of component `s` is `[0, 99]`.
+	fn quit_sub(s: u32, ) -> Weight {
+		// Minimum execution time: 30_171 nanoseconds.
+		Weight::from_ref_time(33_355_514 as u64)
+			// Standard Error: 1_286
+			.saturating_add(Weight::from_ref_time(114_716 as u64).saturating_mul(s as u64))
+			.saturating_add(T::DbWeight::get().reads(2 as u64))
+			.saturating_add(T::DbWeight::get().writes(2 as u64))
+	}
+}
+
+// For backwards compatibility and tests
+impl WeightInfo for () {
+	// Storage: Identity Registrars (r:1 w:1)
+	/// The range of component `r` is `[1, 19]`.
+	fn add_registrar(r: u32, ) -> Weight {
+		// Minimum execution time: 20_269 nanoseconds.
+		Weight::from_ref_time(21_910_543 as u64)
+			// Standard Error: 4_604
+			.saturating_add(Weight::from_ref_time(223_104 as u64).saturating_mul(r as u64))
+			.saturating_add(RocksDbWeight::get().reads(1 as u64))
+			.saturating_add(RocksDbWeight::get().writes(1 as u64))
+	}
+	// Storage: Identity IdentityOf (r:1 w:1)
+	/// The range of component `r` is `[1, 20]`.
+	/// The range of component `x` is `[0, 100]`.
+	fn set_identity(r: u32, x: u32, ) -> Weight {
+		// Minimum execution time: 41_872 nanoseconds.
+		Weight::from_ref_time(40_230_216 as u64)
+			// Standard Error: 2_342
+			.saturating_add(Weight::from_ref_time(145_168 as u64).saturating_mul(r as u64))
+			// Standard Error: 457
+			.saturating_add(Weight::from_ref_time(291_732 as u64).saturating_mul(x as u64))
+			.saturating_add(RocksDbWeight::get().reads(1 as u64))
+			.saturating_add(RocksDbWeight::get().writes(1 as u64))
+	}
+	// Storage: Identity IdentityOf (r:1 w:0)
+	// Storage: Identity SubsOf (r:1 w:1)
+	// Storage: Identity SuperOf (r:2 w:2)
+	/// The range of component `s` is `[0, 100]`.
+	fn set_subs_new(s: u32, ) -> Weight {
+		// Minimum execution time: 12_024 nanoseconds.
+		Weight::from_ref_time(32_550_819 as u64)
+			// Standard Error: 5_057
+			.saturating_add(Weight::from_ref_time(2_521_245 as u64).saturating_mul(s as u64))
+			.saturating_add(RocksDbWeight::get().reads(2 as u64))
+			.saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(s as u64)))
+			.saturating_add(RocksDbWeight::get().writes(1 as u64))
+			.saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(s as u64)))
+	}
+	// Storage: Identity IdentityOf (r:1 w:0)
+	// Storage: Identity SubsOf (r:1 w:1)
+	// Storage: Identity SuperOf (r:0 w:2)
+	/// The range of component `p` is `[0, 100]`.
+	fn set_subs_old(p: u32, ) -> Weight {
+		// Minimum execution time: 12_232 nanoseconds.
+		Weight::from_ref_time(34_009_761 as u64)
+			// Standard Error: 5_047
+			.saturating_add(Weight::from_ref_time(1_113_100 as u64).saturating_mul(p as u64))
+			.saturating_add(RocksDbWeight::get().reads(2 as u64))
+			.saturating_add(RocksDbWeight::get().writes(1 as u64))
+			.saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(p as u64)))
+	}
+	// Storage: Identity SubsOf (r:1 w:1)
+	// Storage: Identity IdentityOf (r:1 w:1)
+	// Storage: Identity SuperOf (r:0 w:100)
+	/// The range of component `r` is `[1, 20]`.
+	/// The range of component `s` is `[0, 100]`.
+	/// The range of component `x` is `[0, 100]`.
+	fn clear_identity(r: u32, s: u32, x: u32, ) -> Weight {
+		// Minimum execution time: 57_144 nanoseconds.
+		Weight::from_ref_time(41_559_247 as u64)
+			// Standard Error: 9_996
+			.saturating_add(Weight::from_ref_time(146_770 as u64).saturating_mul(r as u64))
+			// Standard Error: 1_952
+			.saturating_add(Weight::from_ref_time(1_086_673 as u64).saturating_mul(s as u64))
+			// Standard Error: 1_952
+			.saturating_add(Weight::from_ref_time(162_481 as u64).saturating_mul(x as u64))
+			.saturating_add(RocksDbWeight::get().reads(2 as u64))
+			.saturating_add(RocksDbWeight::get().writes(2 as u64))
+			.saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(s as u64)))
+	}
+	// Storage: Identity Registrars (r:1 w:0)
+	// Storage: Identity IdentityOf (r:1 w:1)
+	/// The range of component `r` is `[1, 20]`.
+	/// The range of component `x` is `[0, 100]`.
+	fn request_judgement(r: u32, x: u32, ) -> Weight {
+		// Minimum execution time: 44_726 nanoseconds.
+		Weight::from_ref_time(41_637_308 as u64)
+			// Standard Error: 1_907
+			.saturating_add(Weight::from_ref_time(219_078 as u64).saturating_mul(r as u64))
+			// Standard Error: 372
+			.saturating_add(Weight::from_ref_time(309_888 as u64).saturating_mul(x as u64))
+			.saturating_add(RocksDbWeight::get().reads(2 as u64))
+			.saturating_add(RocksDbWeight::get().writes(1 as u64))
+	}
+	// Storage: Identity IdentityOf (r:1 w:1)
+	/// The range of component `r` is `[1, 20]`.
+	/// The range of component `x` is `[0, 100]`.
+	fn cancel_request(r: u32, x: u32, ) -> Weight {
+		// Minimum execution time: 39_719 nanoseconds.
+		Weight::from_ref_time(38_008_751 as u64)
+			// Standard Error: 2_394
+			.saturating_add(Weight::from_ref_time(181_870 as u64).saturating_mul(r as u64))
+			// Standard Error: 467
+			.saturating_add(Weight::from_ref_time(314_990 as u64).saturating_mul(x as u64))
+			.saturating_add(RocksDbWeight::get().reads(1 as u64))
+			.saturating_add(RocksDbWeight::get().writes(1 as u64))
+	}
+	// Storage: Identity Registrars (r:1 w:1)
+	/// The range of component `r` is `[1, 19]`.
+	fn set_fee(r: u32, ) -> Weight {
+		// Minimum execution time: 10_634 nanoseconds.
+		Weight::from_ref_time(11_383_704 as u64)
+			// Standard Error: 2_250
+			.saturating_add(Weight::from_ref_time(193_094 as u64).saturating_mul(r as u64))
+			.saturating_add(RocksDbWeight::get().reads(1 as u64))
+			.saturating_add(RocksDbWeight::get().writes(1 as u64))
+	}
+	// Storage: Identity Registrars (r:1 w:1)
+	/// The range of component `r` is `[1, 19]`.
+	fn set_account_id(r: u32, ) -> Weight {
+		// Minimum execution time: 10_840 nanoseconds.
+		Weight::from_ref_time(11_638_740 as u64)
+			// Standard Error: 1_985
+			.saturating_add(Weight::from_ref_time(193_016 as u64).saturating_mul(r as u64))
+			.saturating_add(RocksDbWeight::get().reads(1 as u64))
+			.saturating_add(RocksDbWeight::get().writes(1 as u64))
+	}
+	// Storage: Identity Registrars (r:1 w:1)
+	/// The range of component `r` is `[1, 19]`.
+	fn set_fields(r: u32, ) -> Weight {
+		// Minimum execution time: 10_748 nanoseconds.
+		Weight::from_ref_time(11_346_901 as u64)
+			// Standard Error: 2_132
+			.saturating_add(Weight::from_ref_time(196_630 as u64).saturating_mul(r as u64))
+			.saturating_add(RocksDbWeight::get().reads(1 as u64))
+			.saturating_add(RocksDbWeight::get().writes(1 as u64))
+	}
+	// Storage: Identity Registrars (r:1 w:0)
+	// Storage: Identity IdentityOf (r:1 w:1)
+	/// The range of component `r` is `[1, 19]`.
+	/// The range of component `x` is `[0, 100]`.
+	fn provide_judgement(r: u32, x: u32, ) -> Weight {
+		// Minimum execution time: 33_682 nanoseconds.
+		Weight::from_ref_time(31_336_603 as u64)
+			// Standard Error: 3_056
+			.saturating_add(Weight::from_ref_time(200_403 as u64).saturating_mul(r as u64))
+			// Standard Error: 565
+			.saturating_add(Weight::from_ref_time(525_142 as u64).saturating_mul(x as u64))
+			.saturating_add(RocksDbWeight::get().reads(2 as u64))
+			.saturating_add(RocksDbWeight::get().writes(1 as u64))
+	}
+	// Storage: Identity SubsOf (r:1 w:1)
+	// Storage: Identity IdentityOf (r:1 w:1)
+	// Storage: System Account (r:1 w:1)
+	// Storage: Identity SuperOf (r:0 w:100)
+	/// The range of component `r` is `[1, 20]`.
+	/// The range of component `s` is `[0, 100]`.
+	/// The range of component `x` is `[0, 100]`.
+	fn kill_identity(r: u32, s: u32, x: u32, ) -> Weight {
+		// Minimum execution time: 68_794 nanoseconds.
+		Weight::from_ref_time(52_114_486 as u64)
+			// Standard Error: 4_808
+			.saturating_add(Weight::from_ref_time(153_462 as u64).saturating_mul(r as u64))
+			// Standard Error: 939
+			.saturating_add(Weight::from_ref_time(1_084_612 as u64).saturating_mul(s as u64))
+			// Standard Error: 939
+			.saturating_add(Weight::from_ref_time(170_112 as u64).saturating_mul(x as u64))
+			.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(s as u64)))
+	}
+	// Storage: Identity IdentityOf (r:1 w:1)
+	/// The range of component `x` is `[0, 100]`.
+	/// The range of component `n` is `[0, 600]`.
+	fn force_insert_identities(x: u32, n: u32) -> Weight {
+		// Minimum execution time: 41_872 nanoseconds.
+		Weight::from_ref_time(40_230_216 as u64)
+			// Standard Error: 2_342
+			.saturating_add(Weight::from_ref_time(145_168 as u64))
+			// Standard Error: 457
+			.saturating_add(Weight::from_ref_time(291_732 as u64).saturating_mul(x as u64))
+			.saturating_add(RocksDbWeight::get().reads(1 as u64))
+			.saturating_add(RocksDbWeight::get().writes(1 as u64).saturating_mul(n as u64))
+	}
+	// Storage: Identity IdentityOf (r:1 w:1)
+	/// The range of component `x` is `[0, 100]`.
+	/// The range of component `n` is `[0, 600]`.
+	fn force_remove_identities(x: u32, n: u32) -> Weight {
+		// Minimum execution time: 41_872 nanoseconds.
+		Weight::from_ref_time(40_230_216 as u64)
+			// Standard Error: 2_342
+			.saturating_add(Weight::from_ref_time(145_168 as u64))
+			// Standard Error: 457
+			.saturating_add(Weight::from_ref_time(291_732 as u64).saturating_mul(x as u64))
+			.saturating_add(RocksDbWeight::get().reads(1 as u64))
+			.saturating_add(RocksDbWeight::get().writes(1 as u64).saturating_mul(n as u64))
+	}
+	// Storage: Identity IdentityOf (r:1 w:0)
+	// Storage: Identity SuperOf (r:1 w:1)
+	// Storage: Identity SubsOf (r:1 w:1)
+	/// The range of component `s` is `[0, 99]`.
+	fn add_sub(s: u32, ) -> Weight {
+		// Minimum execution time: 37_914 nanoseconds.
+		Weight::from_ref_time(43_488_083 as u64)
+			// Standard Error: 1_631
+			.saturating_add(Weight::from_ref_time(118_845 as u64).saturating_mul(s as u64))
+			.saturating_add(RocksDbWeight::get().reads(3 as u64))
+			.saturating_add(RocksDbWeight::get().writes(2 as u64))
+	}
+	// Storage: Identity IdentityOf (r:1 w:0)
+	// Storage: Identity SuperOf (r:1 w:1)
+	/// The range of component `s` is `[1, 100]`.
+	fn rename_sub(s: u32, ) -> Weight {
+		// Minimum execution time: 16_124 nanoseconds.
+		Weight::from_ref_time(18_580_462 as u64)
+			// Standard Error: 688
+			.saturating_add(Weight::from_ref_time(67_220 as u64).saturating_mul(s as u64))
+			.saturating_add(RocksDbWeight::get().reads(2 as u64))
+			.saturating_add(RocksDbWeight::get().writes(1 as u64))
+	}
+	// Storage: Identity IdentityOf (r:1 w:0)
+	// Storage: Identity SuperOf (r:1 w:1)
+	// Storage: Identity SubsOf (r:1 w:1)
+	/// The range of component `s` is `[1, 100]`.
+	fn remove_sub(s: u32, ) -> Weight {
+		// Minimum execution time: 41_517 nanoseconds.
+		Weight::from_ref_time(45_123_530 as u64)
+			// Standard Error: 1_530
+			.saturating_add(Weight::from_ref_time(105_429 as u64).saturating_mul(s as u64))
+			.saturating_add(RocksDbWeight::get().reads(3 as u64))
+			.saturating_add(RocksDbWeight::get().writes(2 as u64))
+	}
+	// Storage: Identity SuperOf (r:1 w:1)
+	// Storage: Identity SubsOf (r:1 w:1)
+	/// The range of component `s` is `[0, 99]`.
+	fn quit_sub(s: u32, ) -> Weight {
+		// Minimum execution time: 30_171 nanoseconds.
+		Weight::from_ref_time(33_355_514 as u64)
+			// Standard Error: 1_286
+			.saturating_add(Weight::from_ref_time(114_716 as u64).saturating_mul(s as u64))
+			.saturating_add(RocksDbWeight::get().reads(2 as u64))
+			.saturating_add(RocksDbWeight::get().writes(2 as u64))
+	}
+}
modifiedprimitives/common/src/constants.rsdiffbeforeafterboth
--- a/primitives/common/src/constants.rs
+++ b/primitives/common/src/constants.rs
@@ -42,6 +42,15 @@
 pub const CENTIUNIQUE: Balance = 10 * MILLIUNIQUE;
 pub const UNIQUE: Balance = 100 * CENTIUNIQUE;
 
+/// Minimum balance required to create or keep an account open.
+pub const EXISTENTIAL_DEPOSIT: u128 = 0;
+/// Amount of Balance reserved for candidate registration.
+pub const GENESIS_LICENSE_BOND: u128 = 1_000_000_000_000 * UNIQUE;
+/// Amount of maximum collators for Collator Selection.
+pub const MAX_COLLATORS: u32 = 10;
+/// How long a periodic session lasts in blocks.
+pub const SESSION_LENGTH: BlockNumber = HOURS;
+
 // Targeting 0.1 UNQ per transfer
 pub const WEIGHT_TO_FEE_COEFF: u64 = /*<weight2fee>*/77_071_069_464_131_590/*</weight2fee>*/;
 
addedruntime/common/config/pallets/collator_selection.rsdiffbeforeafterboth
--- /dev/null
+++ b/runtime/common/config/pallets/collator_selection.rs
@@ -0,0 +1,98 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+use frame_support::{parameter_types, PalletId};
+use frame_system::EnsureRoot;
+use crate::{
+	AccountId, Balance, Balances, BlockNumber, Runtime, RuntimeEvent, Aura, Session, SessionKeys,
+	CollatorSelection, Treasury,
+	config::pallets::{MaxCollators, SessionPeriod, TreasuryAccountId},
+};
+use sp_runtime::Perbill;
+use up_common::constants::{UNIQUE, MILLIUNIQUE};
+
+parameter_types! {
+	pub const SessionOffset: BlockNumber = 0;
+}
+
+impl pallet_session::Config for Runtime {
+	type RuntimeEvent = RuntimeEvent;
+	type ValidatorId = <Self as frame_system::Config>::AccountId;
+	// we don't have stash and controller, thus we don't need the convert as well.
+	type ValidatorIdOf = pallet_collator_selection::IdentityCollator;
+	type ShouldEndSession = pallet_session::PeriodicSessions<SessionPeriod, SessionOffset>;
+	type NextSessionRotation = pallet_session::PeriodicSessions<SessionPeriod, SessionOffset>;
+	type SessionManager = CollatorSelection;
+	// Essentially just Aura, but lets be pedantic.
+	type SessionHandler = <SessionKeys as sp_runtime::traits::OpaqueKeys>::KeyTypeIdProviders;
+	type Keys = SessionKeys;
+	type WeightInfo = pallet_session::weights::SubstrateWeight<Self>; // ();
+}
+
+parameter_types! {
+	pub const UncleGenerations: u32 = 0;
+}
+
+impl pallet_authorship::Config for Runtime {
+	type FindAuthor = pallet_session::FindAccountFromAuthorIndex<Self, Aura>;
+	type UncleGenerations = UncleGenerations;
+	type FilterUncle = ();
+	type EventHandler = CollatorSelection;
+}
+
+parameter_types! {
+	// These do not matter as we forbid non-sudo operations with the identity pallet
+	pub const BasicDeposit: Balance = 10 * UNIQUE;
+	pub const FieldDeposit: Balance = 25 * MILLIUNIQUE;
+	pub const SubAccountDeposit: Balance = 2 * UNIQUE;
+	pub const MaxSubAccounts: u32 = 100;
+	pub const MaxAdditionalFields: u32 = 100;
+	pub const MaxRegistrars: u32 = 20;
+}
+
+impl pallet_identity::Config for Runtime {
+	type RuntimeEvent = RuntimeEvent;
+	type Currency = Balances;
+	type BasicDeposit = BasicDeposit;
+	type FieldDeposit = FieldDeposit;
+	type MaxAdditionalFields = MaxAdditionalFields;
+	type MaxRegistrars = MaxRegistrars;
+	type MaxSubAccounts = MaxSubAccounts;
+	type SubAccountDeposit = SubAccountDeposit;
+	type RegistrarOrigin = EnsureRoot<<Self as frame_system::Config>::AccountId>;
+	type ForceOrigin = EnsureRoot<<Self as frame_system::Config>::AccountId>;
+	type Slashed = Treasury;
+	type WeightInfo = pallet_identity::weights::SubstrateWeight<Runtime>;
+}
+
+parameter_types! {
+	pub const PotId: PalletId = PalletId(*b"PotStake");
+	pub const SlashRatio: Perbill = Perbill::from_percent(100);
+}
+
+impl pallet_collator_selection::Config for Runtime {
+	type RuntimeEvent = RuntimeEvent;
+	// We allow root only to execute privileged collator selection operations.
+	type UpdateOrigin = EnsureRoot<AccountId>;
+	type TreasuryAccountId = TreasuryAccountId;
+	type PotId = PotId;
+	type MaxCollators = MaxCollators;
+	type SlashRatio = SlashRatio;
+	type ValidatorId = <Self as frame_system::Config>::AccountId;
+	type ValidatorIdOf = pallet_collator_selection::IdentityCollator;
+	type ValidatorRegistration = Session;
+	type WeightInfo = pallet_collator_selection::weights::SubstrateWeight<Runtime>;
+}
modifiedruntime/common/config/pallets/mod.rsdiffbeforeafterboth
--- a/runtime/common/config/pallets/mod.rs
+++ b/runtime/common/config/pallets/mod.rs
@@ -47,6 +47,9 @@
 #[cfg(feature = "app-promotion")]
 pub mod app_promotion;
 
+#[cfg(feature = "collator-selection")]
+pub mod collator_selection;
+
 parameter_types! {
 	pub TreasuryAccountId: AccountId = TreasuryModuleId::get().into_account_truncating();
 	pub const CollectionCreationPrice: Balance = 2 * UNIQUE;
@@ -101,14 +104,24 @@
 
 parameter_types! {
 	pub AppPromotionDailyRate: Perbill = Perbill::from_rational(5u32, 10_000);
+	pub const MaxCollators: u32 = MAX_COLLATORS;
+	pub const LicenseBond: Balance = GENESIS_LICENSE_BOND;
+	pub const SessionPeriod: BlockNumber = SESSION_LENGTH;
 	pub const DayRelayBlocks: BlockNumber = RELAY_DAYS;
 }
+
 impl pallet_configuration::Config for Runtime {
+	type RuntimeEvent = RuntimeEvent;
+	type Currency = Balances;
 	type DefaultWeightToFeeCoefficient = ConstU64<{ up_common::constants::WEIGHT_TO_FEE_COEFF }>;
 	type DefaultMinGasPrice = ConstU64<{ up_common::constants::MIN_GAS_PRICE }>;
+	type DefaultCollatorSelectionMaxCollators = MaxCollators;
+	type DefaultCollatorSelectionKickThreshold = SessionPeriod;
+	type DefaultCollatorSelectionLicenseBond = LicenseBond;
 	type MaxXcmAllowedLocations = ConstU32<16>;
 	type AppPromotionDailyRate = AppPromotionDailyRate;
 	type DayRelayBlocks = DayRelayBlocks;
+	type WeightInfo = pallet_configuration::weights::SubstrateWeight<Self>;
 }
 
 impl pallet_maintenance::Config for Runtime {
modifiedruntime/common/config/substrate.rsdiffbeforeafterboth
--- a/runtime/common/config/substrate.rs
+++ b/runtime/common/config/substrate.rs
@@ -131,7 +131,7 @@
 
 parameter_types! {
 	// pub const ExistentialDeposit: u128 = 500;
-	pub const ExistentialDeposit: u128 = 0;
+	pub const ExistentialDeposit: u128 = EXISTENTIAL_DEPOSIT;
 	pub const MaxLocks: u32 = 50;
 	pub const MaxReserves: u32 = 50;
 }
modifiedruntime/common/construct_runtime/mod.rsdiffbeforeafterboth
--- a/runtime/common/construct_runtime/mod.rs
+++ b/runtime/common/construct_runtime/mod.rs
@@ -32,8 +32,17 @@
                 ParachainSystem: cumulus_pallet_parachain_system::{Pallet, Call, Config, Storage, Inherent, Event<T>, ValidateUnsigned} = 20,
                 ParachainInfo: parachain_info::{Pallet, Storage, Config} = 21,
 
-                Aura: pallet_aura::{Pallet, Config<T>} = 22,
-                AuraExt: cumulus_pallet_aura_ext::{Pallet, Config} = 23,
+                #[runtimes(quartz)]
+                Authorship: pallet_authorship::{Pallet, Call, Storage} = 22,
+
+                #[runtimes(quartz)]
+                CollatorSelection: pallet_collator_selection::{Pallet, Call, Storage, Event<T>, Config<T>} = 23,
+
+                #[runtimes(quartz)]
+                Session: pallet_session::{Pallet, Call, Storage, Event, Config<T>} = 24,
+
+                Aura: pallet_aura::{Pallet, Config<T>} = 25,
+                AuraExt: cumulus_pallet_aura_ext::{Pallet, Config} = 26,
 
                 Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>} = 30,
                 RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Pallet, Storage} = 31,
@@ -47,6 +56,9 @@
                 Tokens: orml_tokens = 39,
                 // Contracts: pallet_contracts::{Pallet, Call, Storage, Event<T>} = 38,
 
+                #[runtimes(quartz)]
+                Identity: pallet_identity::{Pallet, Call, Storage, Event<T>} = 40,
+
                 // XCM helpers.
                 XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Call, Storage, Event<T>} = 50,
                 PolkadotXcm: pallet_xcm::{Pallet, Call, Event<T>, Origin} = 51,
@@ -60,7 +72,7 @@
                 // #[runtimes(opal)]
                 // Scheduler: pallet_unique_scheduler_v2::{Pallet, Call, Storage, Event<T>} = 62,
 
-                Configuration: pallet_configuration::{Pallet, Call, Storage} = 63,
+                Configuration: pallet_configuration::{Pallet, Call, Storage, Event<T>} = 63,
 
                 Charging: pallet_charge_transaction::{Pallet, Call, Storage } = 64,
                 // ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage} = 65,
addedruntime/common/identity.rsdiffbeforeafterboth
--- /dev/null
+++ b/runtime/common/identity.rs
@@ -0,0 +1,67 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+use scale_info::TypeInfo;
+use codec::{Encode, Decode};
+use up_common::types::AccountId;
+use crate::RuntimeCall;
+
+use sp_runtime::{
+	traits::{DispatchInfoOf, SignedExtension},
+	transaction_validity::{
+		TransactionValidity, ValidTransaction, InvalidTransaction, TransactionValidityError,
+	},
+};
+
+#[derive(Debug, Encode, Decode, PartialEq, Eq, Clone, TypeInfo)]
+pub struct DisableIdentityCalls;
+
+impl SignedExtension for DisableIdentityCalls {
+	type AccountId = AccountId;
+	type Call = RuntimeCall;
+	type AdditionalSigned = ();
+	type Pre = ();
+
+	const IDENTIFIER: &'static str = "DisableIdentityCalls";
+
+	fn additional_signed(&self) -> Result<Self::AdditionalSigned, TransactionValidityError> {
+		Ok(())
+	}
+
+	fn pre_dispatch(
+		self,
+		who: &Self::AccountId,
+		call: &Self::Call,
+		info: &DispatchInfoOf<Self::Call>,
+		len: usize,
+	) -> Result<Self::Pre, TransactionValidityError> {
+		self.validate(who, call, info, len).map(|_| ())
+	}
+
+	fn validate(
+		&self,
+		_who: &Self::AccountId,
+		call: &Self::Call,
+		_info: &DispatchInfoOf<Self::Call>,
+		_len: usize,
+	) -> TransactionValidity {
+		match call {
+			#[cfg(feature = "collator-selection")]
+			RuntimeCall::Identity(_) => Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),
+			_ => Ok(ValidTransaction::default()),
+		}
+	}
+}
modifiedruntime/common/maintenance.rsdiffbeforeafterboth
--- a/runtime/common/maintenance.rs
+++ b/runtime/common/maintenance.rs
@@ -85,6 +85,12 @@
 					Err(TransactionValidityError::Invalid(InvalidTransaction::Call))
 				}
 
+				#[cfg(feature = "collator-selection")]
+				RuntimeCall::CollatorSelection(_)
+				| RuntimeCall::Authorship(_)
+				| RuntimeCall::Session(_)
+				| RuntimeCall::Identity(_) => Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),
+
 				#[cfg(feature = "pallet-test-utils")]
 				RuntimeCall::TestUtils(_) => Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),
 
modifiedruntime/common/mod.rsdiffbeforeafterboth
--- a/runtime/common/mod.rs
+++ b/runtime/common/mod.rs
@@ -18,6 +18,7 @@
 pub mod construct_runtime;
 pub mod dispatch;
 pub mod ethereum;
+pub mod identity;
 pub mod instance;
 pub mod maintenance;
 pub mod runtime_apis;
@@ -33,7 +34,10 @@
 pub mod tests;
 
 use sp_core::H160;
-use frame_support::traits::{Currency, OnUnbalanced, Imbalance};
+use frame_support::{
+	traits::{Currency, OnUnbalanced, Imbalance},
+	weights::Weight,
+};
 use sp_runtime::{
 	generic,
 	traits::{BlakeTwo256, BlockNumberProvider},
@@ -93,6 +97,7 @@
 	frame_system::CheckNonce<Runtime>,
 	frame_system::CheckWeight<Runtime>,
 	maintenance::CheckMaintenance,
+	identity::DisableIdentityCalls,
 	ChargeTransactionPayment,
 	//pallet_contract_helpers::ContractHelpersExtension<Runtime>,
 	pallet_ethereum::FakeTransactionFinalizer<Runtime>,
@@ -113,6 +118,7 @@
 	frame_system::ChainContext<Runtime>,
 	Runtime,
 	AllPalletsWithSystem,
+	AuraToCollatorSelection,
 >;
 
 type NegativeImbalance = <Balances as Currency<AccountId>>::NegativeImbalance;
@@ -179,3 +185,164 @@
 	/// Transfer tokens to the given account from the Parachain account.
 	TransferToken(XAccountId, XBalance),
 }
+
+pub struct AuraToCollatorSelection;
+impl frame_support::traits::OnRuntimeUpgrade for AuraToCollatorSelection {
+	fn on_runtime_upgrade() -> Weight {
+		#[cfg(feature = "collator-selection")]
+		{
+			use frame_support::{BoundedVec, storage::migration};
+			use sp_runtime::{traits::OpaqueKeys, RuntimeAppPublic};
+			use pallet_session::SessionManager;
+			use crate::config::pallets::MaxCollators;
+
+			let mut weight = <Runtime as frame_system::Config>::DbWeight::get().reads(1);
+
+			let version = migration::get_storage_value::<()>(
+				b"AuraToCollatorSelection",
+				b"StorageVersion",
+				&[],
+			);
+
+			let should_upgrade = match version {
+				None => true,
+				Some(_) => false,
+			};
+
+			if should_upgrade {
+				log::info!(
+					target: "runtime::aura_to_collator_selection",
+					"Running migration of Aura authorities to Collator Selection invulnerables"
+				);
+
+				let invulnerables = pallet_aura::Pallet::<Runtime>::authorities()
+					.iter()
+					.cloned()
+					.filter_map(|authority_id| {
+						weight.saturating_accrue(<Runtime as frame_system::Config>::DbWeight::get().reads_writes(1, 1));
+						let vec = authority_id.clone().to_raw_vec();
+						let slice = vec.as_slice();
+						let array: Option<[u8; 32]> = match slice.try_into() {
+							Ok(a) => Some(a),
+							Err(_) => {
+								log::error!("Failed to convert an Aura authority to a Collator Selection invulnerable: {:?}", authority_id);
+								None
+							},
+						};
+						array.map(|a| (AccountId::from(a), authority_id))
+					})
+					.collect::<Vec<_>>();
+
+				let bounded_invulnerables = BoundedVec::<_, MaxCollators>::try_from(
+					invulnerables
+						.iter()
+						.cloned()
+						.map(|(acc, _)| acc)
+						.collect::<Vec<_>>(),
+				)
+				.expect("Existing collators/invulnerables are more than MaxCollators");
+
+				<pallet_collator_selection::Invulnerables<Runtime>>::put(bounded_invulnerables);
+
+				let keys = invulnerables
+					.into_iter()
+					.map(|(acc, aura)| {
+						(
+							acc.clone(),                        // account id
+							acc,                                // validator id
+							SessionKeys { aura: aura.clone() }, // session keys
+						)
+					})
+					.collect::<Vec<_>>();
+
+				for (account, val, keys) in keys.iter().cloned() {
+					for id in <Runtime as pallet_session::Config>::Keys::key_ids() {
+						<pallet_session::KeyOwner<Runtime>>::insert((*id, keys.get_raw(*id)), &val)
+					}
+					<pallet_session::NextKeys<Runtime>>::insert(&val, &keys);
+					// todo exercise caution, the following is taken from genesis
+					if frame_system::Pallet::<Runtime>::inc_consumers_without_limit(&account)
+						.is_err()
+					{
+						log::warn!(
+							"We have entered an error with incrementing consumers without limit during the migration"
+						);
+						// This will leak a provider reference, however it only happens once (at
+						// genesis) so it's really not a big deal and we assume that the user wants to
+						// do this since it's the only way a non-endowed account can contain a session
+						// key.
+						frame_system::Pallet::<Runtime>::inc_providers(&account);
+					}
+				}
+
+				let initial_validators_0 =
+					<Runtime as pallet_session::Config>::SessionManager::new_session(0)
+						.unwrap_or_else(|| {
+							frame_support::print(
+								"No initial validator provided by `SessionManager`, use \
+							session config keys to generate initial validator set.",
+							);
+							keys.iter().map(|x| x.1.clone()).collect()
+						});
+				/*assert!(
+					!initial_validators_0.is_empty(),
+					"Empty validator set for session 0 in (pseudo) genesis block!"
+				);*/
+
+				let initial_validators_1 =
+					<Runtime as pallet_session::Config>::SessionManager::new_session(1)
+						.unwrap_or_else(|| initial_validators_0.clone());
+				/*assert!(
+					!initial_validators_1.is_empty(),
+					"Empty validator set for session 1 in (pseudo) genesis block!"
+				);*/
+
+				let queued_keys: Vec<_> = initial_validators_1
+					.iter()
+					.cloned()
+					.map(|v| {
+						(
+							v.clone(),
+							<pallet_session::NextKeys<Runtime>>::get(&v)
+								.expect("Validator in session 1 missing keys!"),
+						)
+					})
+					.collect();
+
+				// Tell everyone about the genesis session keys -- Aura must've already initialized it
+				//<Runtime as pallet_session::Config>::SessionHandler::on_genesis_session::<<Runtime as pallet_session::Config>::Keys>(&queued_keys);
+
+				<pallet_session::Validators<Runtime>>::put(initial_validators_0);
+				<pallet_session::QueuedKeys<Runtime>>::put(queued_keys);
+
+				<Runtime as pallet_session::Config>::SessionManager::start_session(0);
+
+				log::info!(
+					target: "runtime::aura_to_collator_selection",
+					"Migration of Aura authorities to Collator Selection invulnerables is complete."
+				);
+
+				migration::put_storage_value::<()>(
+					b"AuraToCollatorSelection",
+					b"StorageVersion",
+					&[],
+					(),
+				);
+
+				weight += <Runtime as frame_system::Config>::DbWeight::get().writes(1)
+			} else {
+				log::info!(
+					target: "runtime::aura_to_collator_selection",
+					"The storage migration has already been flagged as complete. No migration needs to be done.",
+				);
+			}
+
+			weight
+		}
+
+		#[cfg(not(feature = "collator-selection"))]
+		{
+			Weight::zero()
+		}
+	}
+}
modifiedruntime/common/runtime_apis.rsdiffbeforeafterboth
--- a/runtime/common/runtime_apis.rs
+++ b/runtime/common/runtime_apis.rs
@@ -686,6 +686,7 @@
                     list_benchmark!(list, extra, pallet_unique, Unique);
                     list_benchmark!(list, extra, pallet_structure, Structure);
                     list_benchmark!(list, extra, pallet_inflation, Inflation);
+                    list_benchmark!(list, extra, pallet_configuration, Configuration);
 
                     #[cfg(feature = "app-promotion")]
                     list_benchmark!(list, extra, pallet_app_promotion, AppPromotion);
@@ -705,6 +706,12 @@
                     #[cfg(feature = "rmrk")]
                     list_benchmark!(list, extra, pallet_proxy_rmrk_equip, RmrkEquip);
 
+                    #[cfg(feature = "collator-selection")]
+                    list_benchmark!(list, extra, pallet_collator_selection, CollatorSelection);
+
+                    #[cfg(feature = "collator-selection")]
+                    list_benchmark!(list, extra, pallet_identity, Identity);
+
                     #[cfg(feature = "foreign-assets")]
                     list_benchmark!(list, extra, pallet_foreign_assets, ForeignAssets);
 
@@ -749,6 +756,7 @@
                     add_benchmark!(params, batches, pallet_unique, Unique);
                     add_benchmark!(params, batches, pallet_structure, Structure);
                     add_benchmark!(params, batches, pallet_inflation, Inflation);
+                    add_benchmark!(params, batches, pallet_configuration, Configuration);
 
                     #[cfg(feature = "app-promotion")]
                     add_benchmark!(params, batches, pallet_app_promotion, AppPromotion);
@@ -768,6 +776,12 @@
                     #[cfg(feature = "rmrk")]
                     add_benchmark!(params, batches, pallet_proxy_rmrk_equip, RmrkEquip);
 
+                    #[cfg(feature = "collator-selection")]
+                    add_benchmark!(params, batches, pallet_collator_selection, CollatorSelection);
+
+                    #[cfg(feature = "collator-selection")]
+                    add_benchmark!(params, batches, pallet_identity, Identity);
+
                     #[cfg(feature = "foreign-assets")]
                     add_benchmark!(params, batches, pallet_foreign_assets, ForeignAssets);
 
modifiedruntime/common/tests/mod.rsdiffbeforeafterboth
--- a/runtime/common/tests/mod.rs
+++ b/runtime/common/tests/mod.rs
@@ -18,7 +18,7 @@
 use sp_core::{Public, Pair};
 use sp_std::vec;
 use up_common::types::AuraId;
-use crate::{GenesisConfig, ParachainInfoConfig, AuraConfig};
+use crate::{GenesisConfig, ParachainInfoConfig};
 
 pub mod xcm;
 
@@ -28,7 +28,56 @@
 		.public()
 }
 
+#[cfg(feature = "collator-selection")]
 fn new_test_ext(para_id: u32) -> sp_io::TestExternalities {
+	use sp_core::{sr25519};
+	use sp_runtime::traits::{IdentifyAccount, Verify};
+	use crate::{AccountId, Signature, SessionKeys, CollatorSelectionConfig, SessionConfig};
+
+	type AccountPublic = <Signature as Verify>::Signer;
+
+	fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId
+	where
+		AccountPublic: From<<TPublic::Pair as Pair>::Public>,
+	{
+		AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()
+	}
+
+	let accounts = vec!["Alice", "Bob"];
+	let keys = accounts
+		.iter()
+		.map(|&acc| {
+			let account_id = get_account_id_from_seed::<sr25519::Public>(acc);
+			(
+				account_id.clone(),
+				account_id,
+				SessionKeys {
+					aura: get_from_seed::<AuraId>(acc),
+				},
+			)
+		})
+		.collect::<Vec<_>>();
+	let invulnerables = accounts
+		.iter()
+		.map(|acc| get_account_id_from_seed::<sr25519::Public>(acc))
+		.collect::<Vec<_>>();
+
+	let cfg = GenesisConfig {
+		collator_selection: CollatorSelectionConfig { invulnerables },
+		session: SessionConfig { keys },
+		parachain_info: ParachainInfoConfig {
+			parachain_id: para_id.into(),
+		},
+		..GenesisConfig::default()
+	};
+
+	cfg.build_storage().unwrap().into()
+}
+
+#[cfg(not(feature = "collator-selection"))]
+fn new_test_ext(para_id: u32) -> sp_io::TestExternalities {
+	use crate::AuraConfig;
+
 	let cfg = GenesisConfig {
 		aura: AuraConfig {
 			authorities: vec![
modifiedruntime/opal/Cargo.tomldiffbeforeafterboth
--- a/runtime/opal/Cargo.toml
+++ b/runtime/opal/Cargo.toml
@@ -28,6 +28,7 @@
     'pallet-evm-coder-substrate/runtime-benchmarks',
     'pallet-balances/runtime-benchmarks',
     'pallet-timestamp/runtime-benchmarks',
+    'pallet-configuration/runtime-benchmarks',
     'pallet-common/runtime-benchmarks',
     'pallet-structure/runtime-benchmarks',
     'pallet-fungible/runtime-benchmarks',
@@ -39,6 +40,8 @@
     'pallet-unique/runtime-benchmarks',
     'pallet-inflation/runtime-benchmarks',
     'pallet-app-promotion/runtime-benchmarks',
+    'pallet-collator-selection/runtime-benchmarks',
+    'pallet-identity/runtime-benchmarks',
     'pallet-unique-scheduler-v2/runtime-benchmarks',
     'pallet-xcm/runtime-benchmarks',
     'sp-runtime/runtime-benchmarks',
@@ -69,6 +72,10 @@
     'pallet-xcm/try-runtime',
     'cumulus-pallet-xcm/try-runtime',
     'cumulus-pallet-dmp-queue/try-runtime',
+	"pallet-authorship/try-runtime",
+	"pallet-collator-selection/try-runtime",
+	"pallet-session/try-runtime",
+	"pallet-identity/try-runtime",
     'pallet-inflation/try-runtime',
     'pallet-unique/try-runtime',
     'pallet-configuration/try-runtime',
@@ -113,6 +120,10 @@
     # 'pallet-contracts-primitives/std',
     # 'pallet-contracts-rpc-runtime-api/std',
     # 'pallet-contract-helpers/std',
+	"pallet-authorship/std",
+	'pallet-collator-selection/std',
+	"pallet-session/std",
+	'pallet-identity/std',
     'pallet-randomness-collective-flip/std',
     'pallet-sudo/std',
     'pallet-timestamp/std',
@@ -192,6 +203,7 @@
 rmrk = []
 foreign-assets = []
 app-promotion = []
+collator-selection = []
 
 ################################################################################
 # Substrate Dependencies
@@ -255,6 +267,16 @@
 git = "https://github.com/paritytech/substrate"
 branch = "polkadot-v0.9.36"
 
+[dependencies.pallet-authorship]
+default-features = false
+git = 'https://github.com/paritytech/substrate'
+branch = 'polkadot-v0.9.36'
+
+[dependencies.pallet-session]
+default-features = false
+git = 'https://github.com/paritytech/substrate'
+branch = 'polkadot-v0.9.36'
+
 [dependencies.pallet-balances]
 default-features = false
 git = "https://github.com/paritytech/substrate"
@@ -478,6 +500,8 @@
 pallet-inflation = { path = '../../pallets/inflation', default-features = false }
 pallet-app-promotion = { path = '../../pallets/app-promotion', default-features = false }
 up-data-structs = { path = '../../primitives/data-structs', default-features = false }
+pallet-collator-selection = { default-features = false, path = "../../pallets/collator-selection" }
+pallet-identity = { default-features = false, path = "../../pallets/identity" }
 pallet-configuration = { default-features = false, path = "../../pallets/configuration" }
 pallet-common = { default-features = false, path = "../../pallets/common" }
 pallet-structure = { default-features = false, path = "../../pallets/structure" }
modifiedruntime/quartz/Cargo.tomldiffbeforeafterboth
--- a/runtime/quartz/Cargo.toml
+++ b/runtime/quartz/Cargo.toml
@@ -28,6 +28,7 @@
     'pallet-evm-coder-substrate/runtime-benchmarks',
     'pallet-balances/runtime-benchmarks',
     'pallet-timestamp/runtime-benchmarks',
+    'pallet-configuration/runtime-benchmarks',
     'pallet-common/runtime-benchmarks',
     'pallet-structure/runtime-benchmarks',
     'pallet-fungible/runtime-benchmarks',
@@ -38,6 +39,8 @@
     'pallet-unique/runtime-benchmarks',
     'pallet-foreign-assets/runtime-benchmarks',
     'pallet-inflation/runtime-benchmarks',
+    'pallet-collator-selection/runtime-benchmarks',
+    'pallet-identity/runtime-benchmarks',
     'pallet-app-promotion/runtime-benchmarks',
     'pallet-xcm/runtime-benchmarks',
     'sp-runtime/runtime-benchmarks',
@@ -67,6 +70,10 @@
     'pallet-xcm/try-runtime',
     'cumulus-pallet-xcm/try-runtime',
     'cumulus-pallet-dmp-queue/try-runtime',
+	"pallet-authorship/try-runtime",
+	"pallet-collator-selection/try-runtime",
+	"pallet-session/try-runtime",
+	"pallet-identity/try-runtime",
     'pallet-inflation/try-runtime',
     'pallet-unique/try-runtime',
     'pallet-configuration/try-runtime',
@@ -108,6 +115,10 @@
     # 'pallet-contracts-primitives/std',
     # 'pallet-contracts-rpc-runtime-api/std',
     # 'pallet-contract-helpers/std',
+	"pallet-authorship/std",
+	'pallet-collator-selection/std',
+	"pallet-session/std",
+	"pallet-identity/std",
     'pallet-randomness-collective-flip/std',
     'pallet-sudo/std',
     'pallet-timestamp/std',
@@ -170,7 +181,7 @@
     "pallet-maintenance/std",
 ]
 limit-testing = ['pallet-unique/limit-testing', 'up-data-structs/limit-testing']
-quartz-runtime = ['refungible', 'app-promotion', 'foreign-assets']
+quartz-runtime = ['refungible', 'app-promotion', 'collator-selection', 'foreign-assets']
 become-sapphire = []
 pov-estimate = []
 
@@ -178,6 +189,7 @@
 scheduler = []
 rmrk = []
 foreign-assets = []
+collator-selection = []
 app-promotion = []
 
 ################################################################################
@@ -242,6 +254,16 @@
 git = "https://github.com/paritytech/substrate"
 branch = "polkadot-v0.9.36"
 
+[dependencies.pallet-authorship]
+default-features = false
+git = 'https://github.com/paritytech/substrate'
+branch = 'polkadot-v0.9.36'
+
+[dependencies.pallet-session]
+default-features = false
+git = 'https://github.com/paritytech/substrate'
+branch = 'polkadot-v0.9.36'
+
 [dependencies.pallet-balances]
 default-features = false
 git = "https://github.com/paritytech/substrate"
@@ -472,6 +494,8 @@
 pallet-inflation = { path = '../../pallets/inflation', default-features = false }
 pallet-app-promotion = { path = '../../pallets/app-promotion', default-features = false }
 up-data-structs = { path = '../../primitives/data-structs', default-features = false }
+pallet-collator-selection = { default-features = false, path = "../../pallets/collator-selection" }
+pallet-identity = { default-features = false, path = "../../pallets/identity" }
 pallet-configuration = { default-features = false, path = "../../pallets/configuration" }
 pallet-common = { default-features = false, path = "../../pallets/common" }
 pallet-structure = { default-features = false, path = "../../pallets/structure" }
modifiedruntime/unique/Cargo.tomldiffbeforeafterboth
--- a/runtime/unique/Cargo.toml
+++ b/runtime/unique/Cargo.toml
@@ -28,6 +28,7 @@
     'pallet-evm-coder-substrate/runtime-benchmarks',
     'pallet-balances/runtime-benchmarks',
     'pallet-timestamp/runtime-benchmarks',
+    'pallet-configuration/runtime-benchmarks',
     'pallet-common/runtime-benchmarks',
     'pallet-structure/runtime-benchmarks',
     'pallet-fungible/runtime-benchmarks',
@@ -38,6 +39,8 @@
     'pallet-unique/runtime-benchmarks',
     'pallet-foreign-assets/runtime-benchmarks',
     'pallet-inflation/runtime-benchmarks',
+    'pallet-collator-selection/runtime-benchmarks',
+    'pallet-identity/runtime-benchmarks',
     'pallet-app-promotion/runtime-benchmarks',
     'pallet-xcm/runtime-benchmarks',
     'sp-runtime/runtime-benchmarks',
@@ -67,6 +70,10 @@
     'pallet-xcm/try-runtime',
     'cumulus-pallet-xcm/try-runtime',
     'cumulus-pallet-dmp-queue/try-runtime',
+	"pallet-authorship/try-runtime",
+	"pallet-collator-selection/try-runtime",
+	"pallet-session/try-runtime",
+	"pallet-identity/try-runtime",
     'pallet-inflation/try-runtime',
     'pallet-unique/try-runtime',
     'pallet-configuration/try-runtime',
@@ -108,6 +115,10 @@
     # 'pallet-contracts-primitives/std',
     # 'pallet-contracts-rpc-runtime-api/std',
     # 'pallet-contract-helpers/std',
+	"pallet-authorship/std",
+	'pallet-collator-selection/std',
+	"pallet-session/std",
+	"pallet-identity/std",
     'pallet-randomness-collective-flip/std',
     'pallet-sudo/std',
     'pallet-timestamp/std',
@@ -178,6 +189,7 @@
 scheduler = []
 rmrk = []
 foreign-assets = []
+collator-selection = []
 
 ################################################################################
 # Substrate Dependencies
@@ -241,6 +253,16 @@
 git = "https://github.com/paritytech/substrate"
 branch = "polkadot-v0.9.36"
 
+[dependencies.pallet-authorship]
+default-features = false
+git = 'https://github.com/paritytech/substrate'
+branch = 'polkadot-v0.9.36'
+
+[dependencies.pallet-session]
+default-features = false
+git = 'https://github.com/paritytech/substrate'
+branch = 'polkadot-v0.9.36'
+
 [dependencies.pallet-balances]
 default-features = false
 git = "https://github.com/paritytech/substrate"
@@ -462,6 +484,8 @@
 pallet-inflation = { path = '../../pallets/inflation', default-features = false }
 pallet-app-promotion = { path = '../../pallets/app-promotion', default-features = false }
 up-data-structs = { path = '../../primitives/data-structs', default-features = false }
+pallet-collator-selection = { default-features = false, path = "../../pallets/collator-selection" }
+pallet-identity = { default-features = false, path = "../../pallets/identity" }
 pallet-configuration = { default-features = false, path = "../../pallets/configuration" }
 pallet-common = { default-features = false, path = "../../pallets/common" }
 pallet-structure = { default-features = false, path = "../../pallets/structure" }
modifiedtests/package.jsondiffbeforeafterboth
--- a/tests/package.json
+++ b/tests/package.json
@@ -31,6 +31,7 @@
     "lint": "eslint --ext .ts,.js src/",
     "fix": "eslint --ext .ts,.js src/ --fix",
     "setup": "ts-node ./src/util/globalSetup.ts",
+    "setIdentities": "ts-node ./src/util/identitySetter.ts",
     "test": "yarn setup && mocha --timeout 9999999 -r ts-node/register './src/**/*.*test.ts'",
     "testParallelFull": "yarn testParallel && yarn testSequential",
     "testParallel": "yarn setup && mocha --parallel --timeout 9999999 -r ts-node/register './src/**/*.test.ts'",
@@ -87,6 +88,8 @@
     "testSchedulingEVM": "mocha --timeout 9999999 -r ts-node/register ./**/eth/scheduling.test.ts",
     "testPalletPresence": "mocha --timeout 9999999 -r ts-node/register ./**/pallet-presence.test.ts",
     "testBlockProduction": "mocha --timeout 9999999 -r ts-node/register ./**/block-production.test.ts",
+    "testCollatorSelection": "mocha --timeout 9999999 -r ts-node/register ./**/collatorSelection.*test.ts",
+    "testIdentity": "mocha --timeout 9999999 -r ts-node/register ./**/identity.*test.ts",
     "testEnableDisableTransfers": "mocha --timeout 9999999 -r ts-node/register ./**/enableDisableTransfer.test.ts",
     "testLimits": "mocha --timeout 9999999 -r ts-node/register ./**/limits.test.ts",
     "testEthCreateNFTCollection": "mocha --timeout 9999999 -r ts-node/register ./**/eth/createNFTCollection.test.ts",
modifiedtests/src/.outdated/substrate/substrate-api.tsdiffbeforeafterboth
--- a/tests/src/.outdated/substrate/substrate-api.ts
+++ b/tests/src/.outdated/substrate/substrate-api.ts
@@ -41,6 +41,10 @@
         extrinsic: {},
         payload: {},
       },
+      DisableIdentityCalls: {
+        extrinsic: {},
+        payload: {},
+      },
       FakeTransactionFinalizer: {
         extrinsic: {},
         payload: {},
addedtests/src/collatorSelection.seqtest.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/collatorSelection.seqtest.ts
@@ -0,0 +1,465 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {IKeyringPair} from '@polkadot/types/types';
+import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from './util';
+
+async function resetInvulnerables() {
+  await usingPlaygrounds(async (helper, privateKey) => {
+    const superuser = await privateKey('//Alice');
+    const alice = await privateKey('//Alice');
+    const bob = await privateKey('//Bob');
+    const invulnerables = await helper.collatorSelection.getInvulnerables();
+    if (!invulnerables.includes(alice.address) || !invulnerables.includes(bob.address) || invulnerables.length != 2) {
+      console.warn('Alice and Bob are not the invulnerables! Reinstating them back. '
+        + 'Current invulnerables\' size: ' + invulnerables.length);
+
+      let nonce = await helper.chain.getNonce(alice.address);
+      // In case there are too many invulnerables already, remove some of them, leaving space for Alice and Bob.
+      if (invulnerables.length + 2 >= helper.collatorSelection.maxCollators()) {
+        await Promise.all([
+          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerables.pop()], true, {nonce: nonce++}),
+          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerables.pop()], true, {nonce: nonce++}),
+        ]);
+      }
+
+      nonce = await helper.chain.getNonce(alice.address);
+      await Promise.all([
+        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: nonce++}),
+        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: nonce++}),
+      ]);
+
+      nonce = await helper.chain.getNonce(alice.address);
+      await Promise.all(invulnerables.map((invulnerable: any) => {
+        if (invulnerable == alice.address || invulnerable == bob.address) return new Promise<void>(res => res());
+        return helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++});
+      }));
+    }
+  });
+}
+
+// todo:collator Most preferable to launch this test in parallel somehow -- or change the session period (1 hr).
+describe('Integration Test: Collator Selection', () => {
+  let superuser: IKeyringPair;
+  let previousLicenseBond = 0n;
+  let licenseBond = 0n;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.CollatorSelection]);
+      superuser = await privateKey('//Alice');
+
+      previousLicenseBond = await helper.collatorSelection.getLicenseBond();
+      licenseBond = 10n * helper.balance.getOneTokenNominal();
+      await helper.getSudo().collatorSelection.setLicenseBond(superuser, licenseBond);
+    });
+  });
+
+  describe('Dynamic shuffling of collators', () => {
+    // These two are the default invulnerables, and should return to be invulnerables after this suite.
+    let alice: IKeyringPair;
+    let bob: IKeyringPair;
+
+    let charlie: IKeyringPair;
+    let dave: IKeyringPair;
+
+    before(async function() {
+      await usingPlaygrounds(async (helper, privateKey) => {
+        // todo:collator see again if blocks start to be finalized in dev mode
+        // Skip the collator block production in dev mode, since the blocks are sealed automatically.
+        if (await helper.arrange.isDevNode()) this.skip();
+
+        alice = await privateKey('//Alice');
+        bob = await privateKey('//Bob');
+        charlie = await privateKey('//Charlie');
+        dave = await privateKey('//Dave');
+
+        expect((await helper.session.setOwnKeysFromAddress(charlie))
+          .status.toLowerCase()).to.be.equal('success');
+        expect((await helper.session.setOwnKeysFromAddress(dave))
+          .status.toLowerCase()).to.be.equal('success');
+
+        const invulnerables = await helper.collatorSelection.getInvulnerables();
+        if (!invulnerables.includes(alice.address) || !invulnerables.includes(bob.address) || invulnerables.length != 2) {
+          console.warn('Alice and Bob are not the invulnerables! Reinstating them back. '
+            + 'Current invulnerables\' size: ' + invulnerables.length);
+
+          let nonce = await helper.chain.getNonce(superuser.address);
+          await Promise.all([
+            helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: nonce++}),
+            helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: nonce++}),
+          ]);
+
+          nonce = await helper.chain.getNonce(superuser.address);
+          await Promise.all(invulnerables.map((invulnerable: any) => {
+            if (invulnerable == alice.address || invulnerable == bob.address) return new Promise((res) => res);
+            return helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++});
+          }));
+        }
+      });
+    });
+
+    itSub('Change invulnerables and make sure they start producing blocks', async ({helper}) => {
+      let nonce = await helper.chain.getNonce(superuser.address);
+      await expect(Promise.all([
+        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [charlie.address], true, {nonce: nonce++}),
+        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [dave.address], true, {nonce: nonce++}),
+      ])).to.be.fulfilled;
+
+      nonce = await helper.chain.getNonce(superuser.address);
+      await expect(Promise.all([
+        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [alice.address], true, {nonce: nonce++}),
+        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [bob.address], true, {nonce: nonce++}),
+      ])).to.be.fulfilled;
+
+      const newInvulnerables = await helper.collatorSelection.getInvulnerables();
+      expect(newInvulnerables).to.contain(charlie.address).and.contain(dave.address).and.be.length(2);
+
+      await helper.wait.newSessions(2);
+
+      const newValidators = await helper.callRpc('api.query.session.validators');
+      expect(newValidators).to.contain(charlie.address).and.contain(dave.address).and.be.length(2);
+
+      const lastBlockNumber = await helper.chain.getLatestBlockNumber();
+      await helper.wait.newBlocks(1);
+      const lastCharlieBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [charlie.address])).toNumber();
+      const lastDaveBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [dave.address])).toNumber();
+      expect(lastCharlieBlock >= lastBlockNumber || lastDaveBlock >= lastBlockNumber).to.be.true;
+    });
+
+    after(async () => {
+      await usingPlaygrounds(async (helper) => {
+        if (await helper.arrange.isDevNode()) return;
+
+        let nonce = await helper.chain.getNonce(superuser.address);
+        await Promise.all([
+          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: nonce++}),
+          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: nonce++}),
+        ]);
+
+        nonce = await helper.chain.getNonce(superuser.address);
+        await Promise.all([
+          await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [charlie.address], true, {nonce: nonce++}),
+          await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [dave.address], true, {nonce: nonce++}),
+        ]);
+      });
+    });
+  });
+
+  describe('Getting and releasing licenses to collate', () => {
+    let charlie: IKeyringPair;
+    let dave: IKeyringPair;
+    let crowd: IKeyringPair[];
+
+    before(async function() {
+      await usingPlaygrounds(async (helper, privateKey) => {
+        charlie = await privateKey('//Charlie');
+        dave = await privateKey('//Dave');
+        crowd = await helper.arrange.createCrowd(20, 100n, superuser);
+
+        // set session keys for everyone
+        expect((await helper.session.setOwnKeysFromAddress(charlie))
+          .status.toLowerCase()).to.be.equal('success');
+        expect((await helper.session.setOwnKeysFromAddress(dave))
+          .status.toLowerCase()).to.be.equal('success');
+        await Promise.all(crowd.map(acc => helper.session.setOwnKeysFromAddress(acc)));
+      });
+    });
+
+    describe('Positive', () => {
+      itSub('Can lease and release a license', async ({helper}) => {
+        const account = crowd.pop()!;
+
+        // make sure it does not have any reserved funds
+        expect((await helper.balance.getSubstrateFull(account.address)).reserved).to.be.equal(0n);
+
+        // getting a license reserves a license bond cost
+        await helper.collatorSelection.obtainLicense(account);
+        expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(licenseBond);
+        expect((await helper.balance.getSubstrateFull(account.address)).reserved).to.be.equal(licenseBond);
+
+        // releasing a license un-reserves the license bond cost
+        await helper.collatorSelection.releaseLicense(account);
+        expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(0n);
+
+        const balance = await helper.balance.getSubstrateFull(account.address);
+        expect(balance.reserved).to.be.equal(0n);
+        expect(balance.free > 100n - licenseBond);
+      });
+
+      itSub('Can force revoke a license', async ({helper}) => {
+        const account = crowd.pop()!;
+
+        // getting a license reserves a license bond cost
+        const previousBalance = await helper.balance.getSubstrateFull(account.address);
+        await helper.collatorSelection.obtainLicense(account);
+        expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(licenseBond);
+
+        // force-releasing a license un-reserves the license bond cost as well
+        await helper.getSudo().collatorSelection.forceReleaseLicense(superuser, account.address);
+        expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(previousBalance.reserved);
+
+        const balance = await helper.balance.getSubstrateFull(account.address);
+        expect(balance.reserved).to.be.equal(previousBalance.reserved);
+        expect(balance.free > previousBalance.free - licenseBond);
+      });
+    });
+
+    describe('Negative', () => {
+      itSub('Cannot get a license without session keys set', async ({helper}) => {
+        const [account] = await helper.arrange.createAccounts([100n], superuser);
+        await expect(helper.collatorSelection.obtainLicense(account))
+          .to.be.rejectedWith(/collatorSelection.ValidatorNotRegistered/);
+      });
+
+      itSub('Cannot register a license twice', async ({helper}) => {
+        const account = crowd.pop()!;
+        await helper.collatorSelection.obtainLicense(account);
+        await expect(helper.collatorSelection.obtainLicense(account))
+          .to.be.rejectedWith(/collatorSelection.AlreadyHoldingLicense/);
+      });
+
+      itSub('Cannot release a license twice', async ({helper}) => {
+        const account = crowd.pop()!;
+        await helper.collatorSelection.obtainLicense(account);
+        await helper.collatorSelection.releaseLicense(account);
+        await expect(helper.collatorSelection.releaseLicense(account))
+          .to.be.rejectedWith(/collatorSelection.NoLicense/);
+      });
+
+      itSub('Cannot force revoke a license as non-sudo', async ({helper}) => {
+        const account = crowd.pop()!;
+        await helper.collatorSelection.obtainLicense(account);
+        await expect(helper.collatorSelection.forceReleaseLicense(superuser, account.address))
+          .to.be.rejectedWith(/BadOrigin/);
+      });
+    });
+  });
+
+  describe('Onboarding, collating, and offboarding as collator candidates', () => {
+    // These two are the default invulnerables, and should return to be invulnerables after this suite.
+    let charlie: IKeyringPair;
+    let dave: IKeyringPair;
+    let crowd: IKeyringPair[];
+
+    before(async function() {
+      await usingPlaygrounds(async (helper, privateKey) => {
+        charlie = await privateKey('//Charlie');
+        dave = await privateKey('//Dave');
+        crowd = await helper.arrange.createCrowd(20, 100n, superuser);
+
+        // set session keys for everyone
+        expect((await helper.session.setOwnKeysFromAddress(charlie))
+          .status.toLowerCase()).to.be.equal('success');
+        expect((await helper.session.setOwnKeysFromAddress(dave))
+          .status.toLowerCase()).to.be.equal('success');
+        await Promise.all(crowd.map(acc => helper.session.setOwnKeysFromAddress(acc)));
+      });
+    });
+
+    describe('Positive', () => {
+      itSub('Can onboard and offboard repeatedly', async ({helper}) => {
+        const account = crowd.pop()!;
+        await helper.collatorSelection.obtainLicense(account);
+        await helper.collatorSelection.onboard(account);
+        expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([account.address]);
+
+        await helper.collatorSelection.offboard(account);
+        expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([]);
+
+        await helper.collatorSelection.onboard(account);
+        expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([account.address]);
+
+        await helper.collatorSelection.offboard(account);
+        expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([]);
+      });
+
+      itSub('Penalizes and forfeits license from faulty collators', async ({helper}) => {
+        // This one shouldn't even be able to produce blocks.
+        const account = crowd.pop()!;
+        await helper.collatorSelection.obtainLicense(account);
+        await helper.collatorSelection.onboard(account);
+        expect(await helper.collatorSelection.getCandidates()).to.contain(account.address);
+
+        // Wait for 3 new sessions before checking that the collator will be kicked:
+        // one to get collator onboarded, and another two for the collator to fail
+        await helper.wait.newSessions(3);
+
+        expect(await helper.collatorSelection.getCandidates()).to.not.contain(account.address);
+        expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(0n);
+
+        // The account's reserved funds get slashed as a penalty
+        const balance = await helper.balance.getSubstrateFull(account.address);
+        expect(balance.reserved).to.be.equal(0n);
+        expect(balance.free < 100n - licenseBond);
+      });
+    });
+
+    describe('Negative', () => {
+      itSub('Cannot onboard without a license', async ({helper}) => {
+        const account = crowd.pop()!;
+        await expect(helper.collatorSelection.onboard(account))
+          .to.be.rejectedWith(/collatorSelection.NoLicense/);
+      });
+
+      itSub('Cannot offboard without a license', async ({helper}) => {
+        const account = crowd.pop()!;
+        await expect(helper.collatorSelection.offboard(account))
+          .to.be.rejectedWith(/collatorSelection.NotCandidate/);
+      });
+
+      itSub('Cannot offboard while not onboarded', async ({helper}) => {
+        const account = crowd.pop()!;
+        await helper.collatorSelection.obtainLicense(account);
+        await expect(helper.collatorSelection.offboard(account))
+          .to.be.rejectedWith(/collatorSelection.NotCandidate/);
+      });
+
+      itSub('Cannot onboard while already onboarded', async ({helper}) => {
+        const account = crowd.pop()!;
+        await helper.collatorSelection.obtainLicense(account);
+        await helper.collatorSelection.onboard(account);
+        await expect(helper.collatorSelection.onboard(account))
+          .to.be.rejectedWith(/collatorSelection.AlreadyCandidate/);
+      });
+    });
+  });
+
+  describe('Addition and removal of invulnerables', () => {
+    before(async function() {
+      await resetInvulnerables();
+    });
+
+    describe('Positive', () => {
+      itSub('Adds an invulnerable', async ({helper}) => {
+        const [account] = await helper.arrange.createAccounts([10n], superuser);
+        const invulnerables = await helper.collatorSelection.getInvulnerables();
+
+        await helper.session.setOwnKeysFromAddress(account);
+        await helper.getSudo().collatorSelection.addInvulnerable(superuser, account.address);
+
+        const newInvulnerables = await helper.collatorSelection.getInvulnerables();
+        expect(invulnerables.concat(account.address)).to.have.all.members(newInvulnerables);
+      });
+
+      itSub('Removes an invulnerable', async ({helper}) => {
+        const invulnerables = await helper.collatorSelection.getInvulnerables();
+        const lastInvulnerable = invulnerables.pop()!;
+
+        await helper.getSudo().collatorSelection.removeInvulnerable(superuser, lastInvulnerable);
+        const newInvulnerables = await helper.collatorSelection.getInvulnerables();
+        // invulnerables had its last element removed, so they should be equal
+        expect(newInvulnerables).to.have.all.members(invulnerables);
+      });
+    });
+
+    describe('Negative', () => {
+      itSub('Does not duplicate an invulnerable', async ({helper}) => {
+        const invulnerables = await helper.collatorSelection.getInvulnerables();
+        // adding an already invulnerable should not fail, but should not duplicate it either
+        await expect(helper.getSudo().collatorSelection.addInvulnerable(superuser, invulnerables[0]))
+          .to.be.fulfilled;
+        const newInvulnerables = await helper.collatorSelection.getInvulnerables();
+        expect(newInvulnerables).to.have.all.members(invulnerables);
+      });
+
+      itSub('Cannot remove a non-existent invulnerable', async ({helper}) => {
+        const [account] = await helper.arrange.createAccounts([0n], superuser);
+        await expect(helper.getSudo().collatorSelection.removeInvulnerable(superuser, account.address))
+          .to.be.rejectedWith(/collatorSelection.NotInvulnerable/);
+      });
+
+      itSub('Cannot allow invulnerables to be empty', async ({helper}) => {
+        const invulnerables = await helper.collatorSelection.getInvulnerables();
+        const lastInvulnerable = invulnerables.pop()!;
+
+        let nonce = await helper.chain.getNonce(superuser.address);
+        await Promise.all(invulnerables.map((i: any) =>
+          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [i], true, {nonce: nonce++})));
+
+        await expect(helper.getSudo().collatorSelection.removeInvulnerable(superuser, lastInvulnerable))
+          .to.be.rejectedWith(/collatorSelection.TooFewInvulnerables/);
+
+        const newInvulnerables = await helper.collatorSelection.getInvulnerables();
+        expect(newInvulnerables).to.be.deep.equal([lastInvulnerable]);
+
+        // restore the invulnerables to the previous state
+        nonce = await helper.chain.getNonce(superuser.address);
+        await Promise.all(invulnerables.map((i: any) =>
+          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [i], true, {nonce: nonce++})));
+      });
+
+      itSub('Cannot have too many invulnerables', async ({helper}) => {
+        // todo:collator make sure that there is enough session time for a set of tests
+        // 28 non-functioning collators, teehee.
+
+        const invulnerablesLength = (await helper.collatorSelection.getInvulnerables()).length;
+        const invulnerablesUntilLimit = helper.collatorSelection.maxCollators() - invulnerablesLength;
+        const newInvulnerables = await helper.arrange.createAccounts(Array(invulnerablesUntilLimit).fill(10n), superuser);
+        const [lastInvulnerable] = await helper.arrange.createAccounts([10n], superuser);
+
+        await Promise.all(newInvulnerables.map((i: IKeyringPair) =>
+          helper.session.setOwnKeysFromAddress(i)));
+        await helper.session.setOwnKeysFromAddress(lastInvulnerable);
+
+        let nonce = await helper.chain.getNonce(superuser.address);
+        await Promise.all(newInvulnerables.map((i: IKeyringPair) =>
+          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [i.address], true, {nonce: nonce++})));
+
+        await expect(helper.getSudo().collatorSelection.addInvulnerable(superuser, lastInvulnerable.address))
+          .to.be.rejectedWith(/collatorSelection.TooManyInvulnerables/);
+
+        // restore the invulnerables to the previous state
+        nonce = await helper.chain.getNonce(superuser.address);
+        await Promise.all(newInvulnerables.map((i: IKeyringPair) =>
+          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [i.address], true, {nonce: nonce++})));
+      });
+
+      itSub('Forbids a non-sudo to add an invulnerable', async ({helper}) => {
+        const [account] = await helper.arrange.createAccounts([10n], superuser);
+        const invulnerables = await helper.collatorSelection.getInvulnerables();
+
+        await helper.session.setOwnKeysFromAddress(account);
+        await expect(helper.collatorSelection.addInvulnerable(superuser, account.address))
+          .to.be.rejectedWith(/BadOrigin/);
+
+        const newInvulnerables = await helper.collatorSelection.getInvulnerables();
+        expect(newInvulnerables).to.be.members(invulnerables);
+      });
+
+      itSub('Forbids a non-sudo to remove an invulnerable', async ({helper}) => {
+        const invulnerables = await helper.collatorSelection.getInvulnerables();
+        await expect(helper.collatorSelection.removeInvulnerable(superuser, invulnerables[0]))
+          .to.be.rejectedWith(/BadOrigin/);
+        expect(await helper.collatorSelection.getInvulnerables()).to.have.all.members(invulnerables);
+      });
+    });
+  });
+
+  after(async () => {
+    // eslint-disable-next-line require-await
+    await usingPlaygrounds(async (helper) => {
+      if (helper.fetchMissingPalletNames([Pallets.CollatorSelection]).length != 0) return;
+
+      await helper.getSudo().collatorSelection.setLicenseBond(superuser, previousLicenseBond);
+
+      const candidates = await helper.collatorSelection.getCandidates();
+      let nonce = await helper.chain.getNonce(superuser.address);
+      await Promise.all(candidates.map(candidate =>
+        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.forceReleaseLicense', [candidate], true, {nonce: nonce++})));
+    });
+  });
+});
\ No newline at end of file
addedtests/src/identity.seqtest.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/identity.seqtest.ts
@@ -0,0 +1,101 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {IKeyringPair} from '@polkadot/types/types';
+import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from './util';
+import {UniqueHelper} from './util/playgrounds/unique';
+
+async function getIdentities(helper: UniqueHelper) {
+  const identities: [string, any][] = [];
+  for(const [key, value] of await helper.getApi().query.identity.identityOf.entries())
+    identities.push([(key as any).toHuman(), (value as any).unwrap()]);
+  return identities;
+}
+
+async function getIdentityAccounts(helper: UniqueHelper) {
+  return (await getIdentities(helper)).flatMap(([key, _value]) => key);
+}
+
+describe('Integration Test: Identities Manipulation', () => {
+  let superuser: IKeyringPair;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.Identity]);
+      superuser = await privateKey('//Alice');
+    });
+  });
+
+  itSub('Normal calls do not work', async ({helper}) => {
+    // console.error = () => {};
+    await expect(helper.executeExtrinsic(superuser, 'api.tx.identity.setIdentity', [{info: {display: {Raw: 'Meowser'}}}]))
+      .to.be.rejectedWith(/Transaction call is not expected/);
+  });
+
+  itSub('Sets identities', async ({helper}) => {
+    const oldIdentitiesCount = (await getIdentityAccounts(helper)).length;
+
+    const crowdSize = 10;
+    const crowd = await helper.arrange.createCrowd(crowdSize, 0n, superuser);
+    const identities = crowd.map((acc, i) => [acc.address, {info: {display: {Raw: `accounter #${i}`}}}]);
+    await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [identities]);
+
+    expect((await getIdentityAccounts(helper)).length).to.be.equal(oldIdentitiesCount + crowdSize);
+  });
+
+  itSub('Setting identities does not delete existing but does overwrite', async ({helper}) => {
+    const crowd = await helper.arrange.createCrowd(10, 0n, superuser);
+    const identities = crowd.map((acc, i) => [acc.address, {info: {display: {Raw: `accounter #${i}`}}}]);
+
+    // insert a single identity
+    let singleIdentity = identities.pop()!;
+    await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [[singleIdentity]]);
+
+    const oldIdentitiesCount = (await getIdentityAccounts(helper)).length;
+
+    // change an identity and push it with a few new others
+    singleIdentity = [singleIdentity[0], {info: {display: {Raw: 'something special'}}}];
+    identities.push(singleIdentity);
+    await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [identities]);
+
+    // oldIdentitiesCount + 9 because one identity is overwritten, not inserted on top
+    expect((await getIdentityAccounts(helper)).length).to.be.equal(oldIdentitiesCount + 9);
+    expect((await helper.callRpc('api.query.identity.identityOf', [singleIdentity[0]])).toHuman().info.display)
+      .to.be.deep.equal({Raw: 'something special'});
+  });
+
+  itSub('Removes identities', async ({helper}) => {
+    const crowd = await helper.arrange.createCrowd(10, 0n, superuser);
+    const identities = crowd.map((acc, i) => [acc.address, {info: {display: {Raw: `accounter #${i}`}}}]);
+    await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [identities]);
+    const oldIdentities = await getIdentityAccounts(helper);
+
+    // delete a couple, check that they are no longer there
+    const scapegoats = [crowd.pop()!.address, crowd.pop()!.address];
+    await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceRemoveIdentities', [scapegoats]);
+    const newIdentities = await getIdentityAccounts(helper);
+    expect(newIdentities.concat(scapegoats)).to.be.have.members(oldIdentities);
+  });
+
+  after(async function() {
+    await usingPlaygrounds(async helper => {
+      if (helper.fetchMissingPalletNames([Pallets.Identity]).length != 0) return;
+
+      const identitiesToRemove: string[] = await getIdentityAccounts(helper);
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceRemoveIdentities', [identitiesToRemove]);
+    });
+  });
+});
modifiedtests/src/interfaces/augment-api-consts.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-consts.ts
+++ b/tests/src/interfaces/augment-api-consts.ts
@@ -81,6 +81,9 @@
     configuration: {
       appPromotionDailyRate: Perbill & AugmentedConst<ApiType>;
       dayRelayBlocks: u32 & AugmentedConst<ApiType>;
+      defaultCollatorSelectionKickThreshold: u32 & AugmentedConst<ApiType>;
+      defaultCollatorSelectionLicenseBond: u128 & AugmentedConst<ApiType>;
+      defaultCollatorSelectionMaxCollators: u32 & AugmentedConst<ApiType>;
       defaultMinGasPrice: u64 & AugmentedConst<ApiType>;
       defaultWeightToFeeCoefficient: u64 & AugmentedConst<ApiType>;
       maxXcmAllowedLocations: u32 & AugmentedConst<ApiType>;
modifiedtests/src/interfaces/augment-api-events.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-events.ts
+++ b/tests/src/interfaces/augment-api-events.ts
@@ -194,6 +194,15 @@
        **/
       [key: string]: AugmentedEvent<ApiType>;
     };
+    configuration: {
+      NewCollatorKickThreshold: AugmentedEvent<ApiType, [lengthInBlocks: Option<u32>], { lengthInBlocks: Option<u32> }>;
+      NewCollatorLicenseBond: AugmentedEvent<ApiType, [bondCost: Option<u128>], { bondCost: Option<u128> }>;
+      NewDesiredCollators: AugmentedEvent<ApiType, [desiredCollators: Option<u32>], { desiredCollators: Option<u32> }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
     cumulusXcm: {
       /**
        * Downward message executed with the given outcome.
modifiedtests/src/interfaces/augment-api-query.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-query.ts
+++ b/tests/src/interfaces/augment-api-query.ts
@@ -159,6 +159,9 @@
     };
     configuration: {
       appPromomotionConfigurationOverride: AugmentedQuery<ApiType, () => Observable<PalletConfigurationAppPromotionConfiguration>, []> & QueryableStorageEntry<ApiType, []>;
+      collatorSelectionDesiredCollatorsOverride: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      collatorSelectionKickThresholdOverride: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      collatorSelectionLicenseBondOverride: AugmentedQuery<ApiType, () => Observable<u128>, []> & QueryableStorageEntry<ApiType, []>;
       minGasPriceOverride: AugmentedQuery<ApiType, () => Observable<u64>, []> & QueryableStorageEntry<ApiType, []>;
       weightToFeeCoefficientOverride: AugmentedQuery<ApiType, () => Observable<u64>, []> & QueryableStorageEntry<ApiType, []>;
       xcmAllowedLocationsOverride: AugmentedQuery<ApiType, () => Observable<Option<Vec<XcmV1MultiLocation>>>, []> & QueryableStorageEntry<ApiType, []>;
modifiedtests/src/interfaces/augment-api-tx.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-tx.ts
+++ b/tests/src/interfaces/augment-api-tx.ts
@@ -216,6 +216,9 @@
     };
     configuration: {
       setAppPromotionConfigurationOverride: AugmentedSubmittable<(configuration: PalletConfigurationAppPromotionConfiguration | { recalculationInterval?: any; pendingInterval?: any; intervalIncome?: any; maxStakersPerCalculation?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletConfigurationAppPromotionConfiguration]>;
+      setCollatorSelectionDesiredCollators: AugmentedSubmittable<(max: Option<u32> | null | Uint8Array | u32 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u32>]>;
+      setCollatorSelectionKickThreshold: AugmentedSubmittable<(threshold: Option<u32> | null | Uint8Array | u32 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u32>]>;
+      setCollatorSelectionLicenseBond: AugmentedSubmittable<(amount: Option<u128> | null | Uint8Array | u128 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u128>]>;
       setMinGasPriceOverride: AugmentedSubmittable<(coeff: Option<u64> | null | Uint8Array | u64 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u64>]>;
       setWeightToFeeCoefficientOverride: AugmentedSubmittable<(coeff: Option<u64> | null | Uint8Array | u64 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u64>]>;
       setXcmAllowedLocations: AugmentedSubmittable<(locations: Option<Vec<XcmV1MultiLocation>> | null | Uint8Array | Vec<XcmV1MultiLocation> | (XcmV1MultiLocation | { parents?: any; interior?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Option<Vec<XcmV1MultiLocation>>]>;
modifiedtests/src/interfaces/augment-types.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-types.ts
+++ b/tests/src/interfaces/augment-types.ts
@@ -5,7 +5,7 @@
 // this is required to allow for ambient/previous definitions
 import '@polkadot/types/types/registry';
 
-import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchDispatchClass, FrameSupportDispatchDispatchInfo, FrameSupportDispatchPays, FrameSupportDispatchPerDispatchClassU32, FrameSupportDispatchPerDispatchClassWeight, FrameSupportDispatchPerDispatchClassWeightsPerClass, FrameSupportPalletId, FrameSupportTokensMiscBalanceStatus, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckTxVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeRuntime, OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensModuleCall, OrmlTokensModuleError, OrmlTokensModuleEvent, OrmlTokensReserveData, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, OrmlXtokensModuleCall, OrmlXtokensModuleError, OrmlXtokensModuleEvent, PalletAppPromotionCall, PalletAppPromotionError, PalletAppPromotionEvent, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletConfigurationAppPromotionConfiguration, PalletConfigurationCall, PalletConfigurationError, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersEvent, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletEvmMigrationEvent, PalletForeignAssetsAssetIds, PalletForeignAssetsModuleAssetMetadata, PalletForeignAssetsModuleCall, PalletForeignAssetsModuleError, PalletForeignAssetsModuleEvent, PalletForeignAssetsNativeCurrency, PalletFungibleError, PalletInflationCall, PalletMaintenanceCall, PalletMaintenanceError, PalletMaintenanceEvent, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTestUtilsCall, PalletTestUtilsError, PalletTestUtilsEvent, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletXcmCall, PalletXcmError, PalletXcmEvent, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionValidityInvalidTransaction, SpRuntimeTransactionValidityTransactionValidityError, SpRuntimeTransactionValidityUnknownTransaction, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, SpWeightsWeightV2Weight, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsRpcCollectionFlags, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipStateAccountId32, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, UpDataStructsTokenData, UpPovEstimateRpcPovInfo, UpPovEstimateRpcTrieKeyValue, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAsset, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from './default';
+import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchDispatchClass, FrameSupportDispatchDispatchInfo, FrameSupportDispatchPays, FrameSupportDispatchPerDispatchClassU32, FrameSupportDispatchPerDispatchClassWeight, FrameSupportDispatchPerDispatchClassWeightsPerClass, FrameSupportPalletId, FrameSupportTokensMiscBalanceStatus, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckTxVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeRuntime, OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls, OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensModuleCall, OrmlTokensModuleError, OrmlTokensModuleEvent, OrmlTokensReserveData, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, OrmlXtokensModuleCall, OrmlXtokensModuleError, OrmlXtokensModuleEvent, PalletAppPromotionCall, PalletAppPromotionError, PalletAppPromotionEvent, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletConfigurationAppPromotionConfiguration, PalletConfigurationCall, PalletConfigurationError, PalletConfigurationEvent, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersEvent, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletEvmMigrationEvent, PalletForeignAssetsAssetIds, PalletForeignAssetsModuleAssetMetadata, PalletForeignAssetsModuleCall, PalletForeignAssetsModuleError, PalletForeignAssetsModuleEvent, PalletForeignAssetsNativeCurrency, PalletFungibleError, PalletInflationCall, PalletMaintenanceCall, PalletMaintenanceError, PalletMaintenanceEvent, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTestUtilsCall, PalletTestUtilsError, PalletTestUtilsEvent, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletXcmCall, PalletXcmError, PalletXcmEvent, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionValidityInvalidTransaction, SpRuntimeTransactionValidityTransactionValidityError, SpRuntimeTransactionValidityUnknownTransaction, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, SpWeightsWeightV2Weight, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsRpcCollectionFlags, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipStateAccountId32, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, UpDataStructsTokenData, UpPovEstimateRpcPovInfo, UpPovEstimateRpcTrieKeyValue, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAsset, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from './default';
 import type { Data, StorageKey } from '@polkadot/types';
 import type { BitVec, Bool, Bytes, F32, F64, I128, I16, I256, I32, I64, I8, Json, Null, OptionBool, Raw, Text, Type, U128, U16, U256, U32, U64, U8, USize, bool, f32, f64, i128, i16, i256, i32, i64, i8, u128, u16, u256, u32, u64, u8, usize } from '@polkadot/types-codec';
 import type { AssetApproval, AssetApprovalKey, AssetBalance, AssetDestroyWitness, AssetDetails, AssetMetadata, TAssetBalance, TAssetDepositBalance } from '@polkadot/types/interfaces/assets';
@@ -772,6 +772,7 @@
     Offender: Offender;
     OldV1SessionInfo: OldV1SessionInfo;
     OpalRuntimeRuntime: OpalRuntimeRuntime;
+    OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls: OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls;
     OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance: OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance;
     OpaqueCall: OpaqueCall;
     OpaqueKeyOwnershipProof: OpaqueKeyOwnershipProof;
@@ -831,6 +832,7 @@
     PalletConfigurationAppPromotionConfiguration: PalletConfigurationAppPromotionConfiguration;
     PalletConfigurationCall: PalletConfigurationCall;
     PalletConfigurationError: PalletConfigurationError;
+    PalletConfigurationEvent: PalletConfigurationEvent;
     PalletConstantMetadataLatest: PalletConstantMetadataLatest;
     PalletConstantMetadataV14: PalletConstantMetadataV14;
     PalletErrorMetadataLatest: PalletErrorMetadataLatest;
modifiedtests/src/interfaces/default/types.tsdiffbeforeafterboth
--- a/tests/src/interfaces/default/types.ts
+++ b/tests/src/interfaces/default/types.ts
@@ -692,6 +692,9 @@
 /** @name OpalRuntimeRuntime */
 export interface OpalRuntimeRuntime extends Null {}
 
+/** @name OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls */
+export interface OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls extends Null {}
+
 /** @name OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance */
 export interface OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance extends Null {}
 
@@ -1306,7 +1309,19 @@
   readonly asSetAppPromotionConfigurationOverride: {
     readonly configuration: PalletConfigurationAppPromotionConfiguration;
   } & Struct;
-  readonly type: 'SetWeightToFeeCoefficientOverride' | 'SetMinGasPriceOverride' | 'SetXcmAllowedLocations' | 'SetAppPromotionConfigurationOverride';
+  readonly isSetCollatorSelectionDesiredCollators: boolean;
+  readonly asSetCollatorSelectionDesiredCollators: {
+    readonly max: Option<u32>;
+  } & Struct;
+  readonly isSetCollatorSelectionLicenseBond: boolean;
+  readonly asSetCollatorSelectionLicenseBond: {
+    readonly amount: Option<u128>;
+  } & Struct;
+  readonly isSetCollatorSelectionKickThreshold: boolean;
+  readonly asSetCollatorSelectionKickThreshold: {
+    readonly threshold: Option<u32>;
+  } & Struct;
+  readonly type: 'SetWeightToFeeCoefficientOverride' | 'SetMinGasPriceOverride' | 'SetXcmAllowedLocations' | 'SetAppPromotionConfigurationOverride' | 'SetCollatorSelectionDesiredCollators' | 'SetCollatorSelectionLicenseBond' | 'SetCollatorSelectionKickThreshold';
 }
 
 /** @name PalletConfigurationError */
@@ -1315,6 +1330,23 @@
   readonly type: 'InconsistentConfiguration';
 }
 
+/** @name PalletConfigurationEvent */
+export interface PalletConfigurationEvent extends Enum {
+  readonly isNewDesiredCollators: boolean;
+  readonly asNewDesiredCollators: {
+    readonly desiredCollators: Option<u32>;
+  } & Struct;
+  readonly isNewCollatorLicenseBond: boolean;
+  readonly asNewCollatorLicenseBond: {
+    readonly bondCost: Option<u128>;
+  } & Struct;
+  readonly isNewCollatorKickThreshold: boolean;
+  readonly asNewCollatorKickThreshold: {
+    readonly lengthInBlocks: Option<u32>;
+  } & Struct;
+  readonly type: 'NewDesiredCollators' | 'NewCollatorLicenseBond' | 'NewCollatorKickThreshold';
+}
+
 /** @name PalletEthereumCall */
 export interface PalletEthereumCall extends Enum {
   readonly isTransact: boolean;
modifiedtests/src/interfaces/lookup.tsdiffbeforeafterboth
--- a/tests/src/interfaces/lookup.ts
+++ b/tests/src/interfaces/lookup.ts
@@ -989,7 +989,23 @@
     }
   },
   /**
-   * Lookup89: pallet_common::pallet::Event<T>
+   * Lookup89: pallet_configuration::pallet::Event<T>
+   **/
+  PalletConfigurationEvent: {
+    _enum: {
+      NewDesiredCollators: {
+        desiredCollators: 'Option<u32>',
+      },
+      NewCollatorLicenseBond: {
+        bondCost: 'Option<u128>',
+      },
+      NewCollatorKickThreshold: {
+        lengthInBlocks: 'Option<u32>'
+      }
+    }
+  },
+  /**
+   * Lookup92: pallet_common::pallet::Event<T>
    **/
   PalletCommonEvent: {
     _enum: {
@@ -1018,7 +1034,7 @@
     }
   },
   /**
-   * Lookup92: pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>
+   * Lookup95: pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>
    **/
   PalletEvmAccountBasicCrossAccountIdRepr: {
     _enum: {
@@ -1027,7 +1043,7 @@
     }
   },
   /**
-   * Lookup96: pallet_structure::pallet::Event<T>
+   * Lookup99: pallet_structure::pallet::Event<T>
    **/
   PalletStructureEvent: {
     _enum: {
@@ -1035,7 +1051,7 @@
     }
   },
   /**
-   * Lookup97: pallet_rmrk_core::pallet::Event<T>
+   * Lookup100: pallet_rmrk_core::pallet::Event<T>
    **/
   PalletRmrkCoreEvent: {
     _enum: {
@@ -1112,7 +1128,7 @@
     }
   },
   /**
-   * Lookup98: rmrk_traits::nft::AccountIdOrCollectionNftTuple<sp_core::crypto::AccountId32>
+   * Lookup101: rmrk_traits::nft::AccountIdOrCollectionNftTuple<sp_core::crypto::AccountId32>
    **/
   RmrkTraitsNftAccountIdOrCollectionNftTuple: {
     _enum: {
@@ -1121,7 +1137,7 @@
     }
   },
   /**
-   * Lookup102: pallet_rmrk_equip::pallet::Event<T>
+   * Lookup104: pallet_rmrk_equip::pallet::Event<T>
    **/
   PalletRmrkEquipEvent: {
     _enum: {
@@ -1136,7 +1152,7 @@
     }
   },
   /**
-   * Lookup103: pallet_app_promotion::pallet::Event<T>
+   * Lookup105: pallet_app_promotion::pallet::Event<T>
    **/
   PalletAppPromotionEvent: {
     _enum: {
@@ -1147,7 +1163,7 @@
     }
   },
   /**
-   * Lookup104: pallet_foreign_assets::module::Event<T>
+   * Lookup106: pallet_foreign_assets::module::Event<T>
    **/
   PalletForeignAssetsModuleEvent: {
     _enum: {
@@ -1172,7 +1188,7 @@
     }
   },
   /**
-   * Lookup105: pallet_foreign_assets::module::AssetMetadata<Balance>
+   * Lookup107: pallet_foreign_assets::module::AssetMetadata<Balance>
    **/
   PalletForeignAssetsModuleAssetMetadata: {
     name: 'Bytes',
@@ -1181,7 +1197,7 @@
     minimalBalance: 'u128'
   },
   /**
-   * Lookup106: pallet_evm::pallet::Event<T>
+   * Lookup108: pallet_evm::pallet::Event<T>
    **/
   PalletEvmEvent: {
     _enum: {
@@ -1203,7 +1219,7 @@
     }
   },
   /**
-   * Lookup107: ethereum::log::Log
+   * Lookup109: ethereum::log::Log
    **/
   EthereumLog: {
     address: 'H160',
@@ -1211,7 +1227,7 @@
     data: 'Bytes'
   },
   /**
-   * Lookup109: pallet_ethereum::pallet::Event
+   * Lookup111: pallet_ethereum::pallet::Event
    **/
   PalletEthereumEvent: {
     _enum: {
@@ -1224,7 +1240,7 @@
     }
   },
   /**
-   * Lookup110: evm_core::error::ExitReason
+   * Lookup112: evm_core::error::ExitReason
    **/
   EvmCoreErrorExitReason: {
     _enum: {
@@ -1235,13 +1251,13 @@
     }
   },
   /**
-   * Lookup111: evm_core::error::ExitSucceed
+   * Lookup113: evm_core::error::ExitSucceed
    **/
   EvmCoreErrorExitSucceed: {
     _enum: ['Stopped', 'Returned', 'Suicided']
   },
   /**
-   * Lookup112: evm_core::error::ExitError
+   * Lookup114: evm_core::error::ExitError
    **/
   EvmCoreErrorExitError: {
     _enum: {
@@ -1263,13 +1279,13 @@
     }
   },
   /**
-   * Lookup115: evm_core::error::ExitRevert
+   * Lookup117: evm_core::error::ExitRevert
    **/
   EvmCoreErrorExitRevert: {
     _enum: ['Reverted']
   },
   /**
-   * Lookup116: evm_core::error::ExitFatal
+   * Lookup118: evm_core::error::ExitFatal
    **/
   EvmCoreErrorExitFatal: {
     _enum: {
@@ -1280,7 +1296,7 @@
     }
   },
   /**
-   * Lookup117: pallet_evm_contract_helpers::pallet::Event<T>
+   * Lookup119: pallet_evm_contract_helpers::pallet::Event<T>
    **/
   PalletEvmContractHelpersEvent: {
     _enum: {
@@ -1290,25 +1306,25 @@
     }
   },
   /**
-   * Lookup118: pallet_evm_migration::pallet::Event<T>
+   * Lookup120: pallet_evm_migration::pallet::Event<T>
    **/
   PalletEvmMigrationEvent: {
     _enum: ['TestEvent']
   },
   /**
-   * Lookup119: pallet_maintenance::pallet::Event<T>
+   * Lookup121: pallet_maintenance::pallet::Event<T>
    **/
   PalletMaintenanceEvent: {
     _enum: ['MaintenanceEnabled', 'MaintenanceDisabled']
   },
   /**
-   * Lookup120: pallet_test_utils::pallet::Event<T>
+   * Lookup122: pallet_test_utils::pallet::Event<T>
    **/
   PalletTestUtilsEvent: {
     _enum: ['ValueIsSet', 'ShouldRollback', 'BatchCompleted']
   },
   /**
-   * Lookup121: frame_system::Phase
+   * Lookup123: frame_system::Phase
    **/
   FrameSystemPhase: {
     _enum: {
@@ -1318,14 +1334,14 @@
     }
   },
   /**
-   * Lookup124: frame_system::LastRuntimeUpgradeInfo
+   * Lookup126: frame_system::LastRuntimeUpgradeInfo
    **/
   FrameSystemLastRuntimeUpgradeInfo: {
     specVersion: 'Compact<u32>',
     specName: 'Text'
   },
   /**
-   * Lookup125: frame_system::pallet::Call<T>
+   * Lookup127: frame_system::pallet::Call<T>
    **/
   FrameSystemCall: {
     _enum: {
@@ -1360,7 +1376,7 @@
     }
   },
   /**
-   * Lookup129: frame_system::limits::BlockWeights
+   * Lookup131: frame_system::limits::BlockWeights
    **/
   FrameSystemLimitsBlockWeights: {
     baseBlock: 'SpWeightsWeightV2Weight',
@@ -1368,7 +1384,7 @@
     perClass: 'FrameSupportDispatchPerDispatchClassWeightsPerClass'
   },
   /**
-   * Lookup130: frame_support::dispatch::PerDispatchClass<frame_system::limits::WeightsPerClass>
+   * Lookup132: frame_support::dispatch::PerDispatchClass<frame_system::limits::WeightsPerClass>
    **/
   FrameSupportDispatchPerDispatchClassWeightsPerClass: {
     normal: 'FrameSystemLimitsWeightsPerClass',
@@ -1376,7 +1392,7 @@
     mandatory: 'FrameSystemLimitsWeightsPerClass'
   },
   /**
-   * Lookup131: frame_system::limits::WeightsPerClass
+   * Lookup133: frame_system::limits::WeightsPerClass
    **/
   FrameSystemLimitsWeightsPerClass: {
     baseExtrinsic: 'SpWeightsWeightV2Weight',
@@ -1385,13 +1401,13 @@
     reserved: 'Option<SpWeightsWeightV2Weight>'
   },
   /**
-   * Lookup133: frame_system::limits::BlockLength
+   * Lookup135: frame_system::limits::BlockLength
    **/
   FrameSystemLimitsBlockLength: {
     max: 'FrameSupportDispatchPerDispatchClassU32'
   },
   /**
-   * Lookup134: frame_support::dispatch::PerDispatchClass<T>
+   * Lookup136: frame_support::dispatch::PerDispatchClass<T>
    **/
   FrameSupportDispatchPerDispatchClassU32: {
     normal: 'u32',
@@ -1399,14 +1415,14 @@
     mandatory: 'u32'
   },
   /**
-   * Lookup135: sp_weights::RuntimeDbWeight
+   * Lookup137: sp_weights::RuntimeDbWeight
    **/
   SpWeightsRuntimeDbWeight: {
     read: 'u64',
     write: 'u64'
   },
   /**
-   * Lookup136: sp_version::RuntimeVersion
+   * Lookup138: sp_version::RuntimeVersion
    **/
   SpVersionRuntimeVersion: {
     specName: 'Text',
@@ -1419,13 +1435,13 @@
     stateVersion: 'u8'
   },
   /**
-   * Lookup141: frame_system::pallet::Error<T>
+   * Lookup143: frame_system::pallet::Error<T>
    **/
   FrameSystemError: {
     _enum: ['InvalidSpecName', 'SpecVersionNeedsToIncrease', 'FailedToExtractRuntimeVersion', 'NonDefaultComposite', 'NonZeroRefCount', 'CallFiltered']
   },
   /**
-   * Lookup142: polkadot_primitives::v2::PersistedValidationData<primitive_types::H256, N>
+   * Lookup144: polkadot_primitives::v2::PersistedValidationData<primitive_types::H256, N>
    **/
   PolkadotPrimitivesV2PersistedValidationData: {
     parentHead: 'Bytes',
@@ -1434,19 +1450,19 @@
     maxPovSize: 'u32'
   },
   /**
-   * Lookup145: polkadot_primitives::v2::UpgradeRestriction
+   * Lookup147: polkadot_primitives::v2::UpgradeRestriction
    **/
   PolkadotPrimitivesV2UpgradeRestriction: {
     _enum: ['Present']
   },
   /**
-   * Lookup146: sp_trie::storage_proof::StorageProof
+   * Lookup148: sp_trie::storage_proof::StorageProof
    **/
   SpTrieStorageProof: {
     trieNodes: 'BTreeSet<Bytes>'
   },
   /**
-   * Lookup148: cumulus_pallet_parachain_system::relay_state_snapshot::MessagingStateSnapshot
+   * Lookup150: cumulus_pallet_parachain_system::relay_state_snapshot::MessagingStateSnapshot
    **/
   CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot: {
     dmqMqcHead: 'H256',
@@ -1455,7 +1471,7 @@
     egressChannels: 'Vec<(u32,PolkadotPrimitivesV2AbridgedHrmpChannel)>'
   },
   /**
-   * Lookup151: polkadot_primitives::v2::AbridgedHrmpChannel
+   * Lookup153: polkadot_primitives::v2::AbridgedHrmpChannel
    **/
   PolkadotPrimitivesV2AbridgedHrmpChannel: {
     maxCapacity: 'u32',
@@ -1466,7 +1482,7 @@
     mqcHead: 'Option<H256>'
   },
   /**
-   * Lookup152: polkadot_primitives::v2::AbridgedHostConfiguration
+   * Lookup154: polkadot_primitives::v2::AbridgedHostConfiguration
    **/
   PolkadotPrimitivesV2AbridgedHostConfiguration: {
     maxCodeSize: 'u32',
@@ -1480,14 +1496,14 @@
     validationUpgradeDelay: 'u32'
   },
   /**
-   * Lookup158: polkadot_core_primitives::OutboundHrmpMessage<polkadot_parachain::primitives::Id>
+   * Lookup160: polkadot_core_primitives::OutboundHrmpMessage<polkadot_parachain::primitives::Id>
    **/
   PolkadotCorePrimitivesOutboundHrmpMessage: {
     recipient: 'u32',
     data: 'Bytes'
   },
   /**
-   * Lookup159: cumulus_pallet_parachain_system::pallet::Call<T>
+   * Lookup161: cumulus_pallet_parachain_system::pallet::Call<T>
    **/
   CumulusPalletParachainSystemCall: {
     _enum: {
@@ -1506,7 +1522,7 @@
     }
   },
   /**
-   * Lookup160: cumulus_primitives_parachain_inherent::ParachainInherentData
+   * Lookup162: cumulus_primitives_parachain_inherent::ParachainInherentData
    **/
   CumulusPrimitivesParachainInherentParachainInherentData: {
     validationData: 'PolkadotPrimitivesV2PersistedValidationData',
@@ -1515,27 +1531,27 @@
     horizontalMessages: 'BTreeMap<u32, Vec<PolkadotCorePrimitivesInboundHrmpMessage>>'
   },
   /**
-   * Lookup162: polkadot_core_primitives::InboundDownwardMessage<BlockNumber>
+   * Lookup164: polkadot_core_primitives::InboundDownwardMessage<BlockNumber>
    **/
   PolkadotCorePrimitivesInboundDownwardMessage: {
     sentAt: 'u32',
     msg: 'Bytes'
   },
   /**
-   * Lookup165: polkadot_core_primitives::InboundHrmpMessage<BlockNumber>
+   * Lookup167: polkadot_core_primitives::InboundHrmpMessage<BlockNumber>
    **/
   PolkadotCorePrimitivesInboundHrmpMessage: {
     sentAt: 'u32',
     data: 'Bytes'
   },
   /**
-   * Lookup168: cumulus_pallet_parachain_system::pallet::Error<T>
+   * Lookup170: cumulus_pallet_parachain_system::pallet::Error<T>
    **/
   CumulusPalletParachainSystemError: {
     _enum: ['OverlappingUpgrades', 'ProhibitedByPolkadot', 'TooBig', 'ValidationDataNotAvailable', 'HostConfigurationNotAvailable', 'NotScheduled', 'NothingAuthorized', 'Unauthorized']
   },
   /**
-   * Lookup170: pallet_balances::BalanceLock<Balance>
+   * Lookup172: pallet_balances::BalanceLock<Balance>
    **/
   PalletBalancesBalanceLock: {
     id: '[u8;8]',
@@ -1543,20 +1559,20 @@
     reasons: 'PalletBalancesReasons'
   },
   /**
-   * Lookup171: pallet_balances::Reasons
+   * Lookup173: pallet_balances::Reasons
    **/
   PalletBalancesReasons: {
     _enum: ['Fee', 'Misc', 'All']
   },
   /**
-   * Lookup174: pallet_balances::ReserveData<ReserveIdentifier, Balance>
+   * Lookup176: pallet_balances::ReserveData<ReserveIdentifier, Balance>
    **/
   PalletBalancesReserveData: {
     id: '[u8;16]',
     amount: 'u128'
   },
   /**
-   * Lookup176: pallet_balances::pallet::Call<T, I>
+   * Lookup178: pallet_balances::pallet::Call<T, I>
    **/
   PalletBalancesCall: {
     _enum: {
@@ -1589,13 +1605,13 @@
     }
   },
   /**
-   * Lookup179: pallet_balances::pallet::Error<T, I>
+   * Lookup181: pallet_balances::pallet::Error<T, I>
    **/
   PalletBalancesError: {
     _enum: ['VestingBalance', 'LiquidityRestrictions', 'InsufficientBalance', 'ExistentialDeposit', 'KeepAlive', 'ExistingVestingSchedule', 'DeadAccount', 'TooManyReserves']
   },
   /**
-   * Lookup181: pallet_timestamp::pallet::Call<T>
+   * Lookup183: pallet_timestamp::pallet::Call<T>
    **/
   PalletTimestampCall: {
     _enum: {
@@ -1605,13 +1621,13 @@
     }
   },
   /**
-   * Lookup183: pallet_transaction_payment::Releases
+   * Lookup185: pallet_transaction_payment::Releases
    **/
   PalletTransactionPaymentReleases: {
     _enum: ['V1Ancient', 'V2']
   },
   /**
-   * Lookup184: pallet_treasury::Proposal<sp_core::crypto::AccountId32, Balance>
+   * Lookup186: pallet_treasury::Proposal<sp_core::crypto::AccountId32, Balance>
    **/
   PalletTreasuryProposal: {
     proposer: 'AccountId32',
@@ -1620,7 +1636,7 @@
     bond: 'u128'
   },
   /**
-   * Lookup187: pallet_treasury::pallet::Call<T, I>
+   * Lookup189: pallet_treasury::pallet::Call<T, I>
    **/
   PalletTreasuryCall: {
     _enum: {
@@ -1644,17 +1660,17 @@
     }
   },
   /**
-   * Lookup190: frame_support::PalletId
+   * Lookup191: frame_support::PalletId
    **/
   FrameSupportPalletId: '[u8;8]',
   /**
-   * Lookup191: pallet_treasury::pallet::Error<T, I>
+   * Lookup192: pallet_treasury::pallet::Error<T, I>
    **/
   PalletTreasuryError: {
     _enum: ['InsufficientProposersBalance', 'InvalidIndex', 'TooManyApprovals', 'InsufficientPermission', 'ProposalNotApproved']
   },
   /**
-   * Lookup192: pallet_sudo::pallet::Call<T>
+   * Lookup193: pallet_sudo::pallet::Call<T>
    **/
   PalletSudoCall: {
     _enum: {
@@ -1678,7 +1694,7 @@
     }
   },
   /**
-   * Lookup194: orml_vesting::module::Call<T>
+   * Lookup195: orml_vesting::module::Call<T>
    **/
   OrmlVestingModuleCall: {
     _enum: {
@@ -1697,7 +1713,7 @@
     }
   },
   /**
-   * Lookup196: orml_xtokens::module::Call<T>
+   * Lookup197: orml_xtokens::module::Call<T>
    **/
   OrmlXtokensModuleCall: {
     _enum: {
@@ -1740,7 +1756,7 @@
     }
   },
   /**
-   * Lookup197: xcm::VersionedMultiAsset
+   * Lookup198: xcm::VersionedMultiAsset
    **/
   XcmVersionedMultiAsset: {
     _enum: {
@@ -1749,7 +1765,7 @@
     }
   },
   /**
-   * Lookup200: orml_tokens::module::Call<T>
+   * Lookup201: orml_tokens::module::Call<T>
    **/
   OrmlTokensModuleCall: {
     _enum: {
@@ -1783,7 +1799,7 @@
     }
   },
   /**
-   * Lookup201: cumulus_pallet_xcmp_queue::pallet::Call<T>
+   * Lookup202: cumulus_pallet_xcmp_queue::pallet::Call<T>
    **/
   CumulusPalletXcmpQueueCall: {
     _enum: {
@@ -1832,7 +1848,7 @@
     }
   },
   /**
-   * Lookup202: pallet_xcm::pallet::Call<T>
+   * Lookup203: pallet_xcm::pallet::Call<T>
    **/
   PalletXcmCall: {
     _enum: {
@@ -1886,7 +1902,7 @@
     }
   },
   /**
-   * Lookup203: xcm::VersionedXcm<RuntimeCall>
+   * Lookup204: xcm::VersionedXcm<RuntimeCall>
    **/
   XcmVersionedXcm: {
     _enum: {
@@ -1896,7 +1912,7 @@
     }
   },
   /**
-   * Lookup204: xcm::v0::Xcm<RuntimeCall>
+   * Lookup205: xcm::v0::Xcm<RuntimeCall>
    **/
   XcmV0Xcm: {
     _enum: {
@@ -1950,7 +1966,7 @@
     }
   },
   /**
-   * Lookup206: xcm::v0::order::Order<RuntimeCall>
+   * Lookup207: xcm::v0::order::Order<RuntimeCall>
    **/
   XcmV0Order: {
     _enum: {
@@ -1993,7 +2009,7 @@
     }
   },
   /**
-   * Lookup208: xcm::v0::Response
+   * Lookup209: xcm::v0::Response
    **/
   XcmV0Response: {
     _enum: {
@@ -2001,7 +2017,7 @@
     }
   },
   /**
-   * Lookup209: xcm::v1::Xcm<RuntimeCall>
+   * Lookup210: xcm::v1::Xcm<RuntimeCall>
    **/
   XcmV1Xcm: {
     _enum: {
@@ -2060,7 +2076,7 @@
     }
   },
   /**
-   * Lookup211: xcm::v1::order::Order<RuntimeCall>
+   * Lookup212: xcm::v1::order::Order<RuntimeCall>
    **/
   XcmV1Order: {
     _enum: {
@@ -2105,7 +2121,7 @@
     }
   },
   /**
-   * Lookup213: xcm::v1::Response
+   * Lookup214: xcm::v1::Response
    **/
   XcmV1Response: {
     _enum: {
@@ -2114,11 +2130,11 @@
     }
   },
   /**
-   * Lookup227: cumulus_pallet_xcm::pallet::Call<T>
+   * Lookup228: cumulus_pallet_xcm::pallet::Call<T>
    **/
   CumulusPalletXcmCall: 'Null',
   /**
-   * Lookup228: cumulus_pallet_dmp_queue::pallet::Call<T>
+   * Lookup229: cumulus_pallet_dmp_queue::pallet::Call<T>
    **/
   CumulusPalletDmpQueueCall: {
     _enum: {
@@ -2129,7 +2145,7 @@
     }
   },
   /**
-   * Lookup229: pallet_inflation::pallet::Call<T>
+   * Lookup230: pallet_inflation::pallet::Call<T>
    **/
   PalletInflationCall: {
     _enum: {
@@ -2139,7 +2155,7 @@
     }
   },
   /**
-   * Lookup230: pallet_unique::Call<T>
+   * Lookup231: pallet_unique::Call<T>
    **/
   PalletUniqueCall: {
     _enum: {
@@ -2283,7 +2299,7 @@
     }
   },
   /**
-   * Lookup235: up_data_structs::CollectionMode
+   * Lookup236: up_data_structs::CollectionMode
    **/
   UpDataStructsCollectionMode: {
     _enum: {
@@ -2293,7 +2309,7 @@
     }
   },
   /**
-   * Lookup236: up_data_structs::CreateCollectionData<sp_core::crypto::AccountId32>
+   * Lookup237: up_data_structs::CreateCollectionData<sp_core::crypto::AccountId32>
    **/
   UpDataStructsCreateCollectionData: {
     mode: 'UpDataStructsCollectionMode',
@@ -2308,13 +2324,13 @@
     properties: 'Vec<UpDataStructsProperty>'
   },
   /**
-   * Lookup238: up_data_structs::AccessMode
+   * Lookup239: up_data_structs::AccessMode
    **/
   UpDataStructsAccessMode: {
     _enum: ['Normal', 'AllowList']
   },
   /**
-   * Lookup240: up_data_structs::CollectionLimits
+   * Lookup241: up_data_structs::CollectionLimits
    **/
   UpDataStructsCollectionLimits: {
     accountTokenOwnershipLimit: 'Option<u32>',
@@ -2328,7 +2344,7 @@
     transfersEnabled: 'Option<bool>'
   },
   /**
-   * Lookup242: up_data_structs::SponsoringRateLimit
+   * Lookup243: up_data_structs::SponsoringRateLimit
    **/
   UpDataStructsSponsoringRateLimit: {
     _enum: {
@@ -2337,7 +2353,7 @@
     }
   },
   /**
-   * Lookup245: up_data_structs::CollectionPermissions
+   * Lookup246: up_data_structs::CollectionPermissions
    **/
   UpDataStructsCollectionPermissions: {
     access: 'Option<UpDataStructsAccessMode>',
@@ -2345,7 +2361,7 @@
     nesting: 'Option<UpDataStructsNestingPermissions>'
   },
   /**
-   * Lookup247: up_data_structs::NestingPermissions
+   * Lookup248: up_data_structs::NestingPermissions
    **/
   UpDataStructsNestingPermissions: {
     tokenOwner: 'bool',
@@ -2353,18 +2369,18 @@
     restricted: 'Option<UpDataStructsOwnerRestrictedSet>'
   },
   /**
-   * Lookup249: up_data_structs::OwnerRestrictedSet
+   * Lookup250: up_data_structs::OwnerRestrictedSet
    **/
   UpDataStructsOwnerRestrictedSet: 'BTreeSet<u32>',
   /**
-   * Lookup254: up_data_structs::PropertyKeyPermission
+   * Lookup255: up_data_structs::PropertyKeyPermission
    **/
   UpDataStructsPropertyKeyPermission: {
     key: 'Bytes',
     permission: 'UpDataStructsPropertyPermission'
   },
   /**
-   * Lookup255: up_data_structs::PropertyPermission
+   * Lookup256: up_data_structs::PropertyPermission
    **/
   UpDataStructsPropertyPermission: {
     mutable: 'bool',
@@ -2372,14 +2388,14 @@
     tokenOwner: 'bool'
   },
   /**
-   * Lookup258: up_data_structs::Property
+   * Lookup259: up_data_structs::Property
    **/
   UpDataStructsProperty: {
     key: 'Bytes',
     value: 'Bytes'
   },
   /**
-   * Lookup261: up_data_structs::CreateItemData
+   * Lookup262: up_data_structs::CreateItemData
    **/
   UpDataStructsCreateItemData: {
     _enum: {
@@ -2389,26 +2405,26 @@
     }
   },
   /**
-   * Lookup262: up_data_structs::CreateNftData
+   * Lookup263: up_data_structs::CreateNftData
    **/
   UpDataStructsCreateNftData: {
     properties: 'Vec<UpDataStructsProperty>'
   },
   /**
-   * Lookup263: up_data_structs::CreateFungibleData
+   * Lookup264: up_data_structs::CreateFungibleData
    **/
   UpDataStructsCreateFungibleData: {
     value: 'u128'
   },
   /**
-   * Lookup264: up_data_structs::CreateReFungibleData
+   * Lookup265: up_data_structs::CreateReFungibleData
    **/
   UpDataStructsCreateReFungibleData: {
     pieces: 'u128',
     properties: 'Vec<UpDataStructsProperty>'
   },
   /**
-   * Lookup267: up_data_structs::CreateItemExData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   * Lookup268: up_data_structs::CreateItemExData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
    **/
   UpDataStructsCreateItemExData: {
     _enum: {
@@ -2419,14 +2435,14 @@
     }
   },
   /**
-   * Lookup269: up_data_structs::CreateNftExData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   * Lookup270: up_data_structs::CreateNftExData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
    **/
   UpDataStructsCreateNftExData: {
     properties: 'Vec<UpDataStructsProperty>',
     owner: 'PalletEvmAccountBasicCrossAccountIdRepr'
   },
   /**
-   * Lookup276: up_data_structs::CreateRefungibleExSingleOwner<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   * Lookup277: up_data_structs::CreateRefungibleExSingleOwner<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
    **/
   UpDataStructsCreateRefungibleExSingleOwner: {
     user: 'PalletEvmAccountBasicCrossAccountIdRepr',
@@ -2434,14 +2450,14 @@
     properties: 'Vec<UpDataStructsProperty>'
   },
   /**
-   * Lookup278: up_data_structs::CreateRefungibleExMultipleOwners<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   * Lookup279: up_data_structs::CreateRefungibleExMultipleOwners<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
    **/
   UpDataStructsCreateRefungibleExMultipleOwners: {
     users: 'BTreeMap<PalletEvmAccountBasicCrossAccountIdRepr, u128>',
     properties: 'Vec<UpDataStructsProperty>'
   },
   /**
-   * Lookup279: pallet_configuration::pallet::Call<T>
+   * Lookup280: pallet_configuration::pallet::Call<T>
    **/
   PalletConfigurationCall: {
     _enum: {
@@ -2455,12 +2471,21 @@
         locations: 'Option<Vec<XcmV1MultiLocation>>',
       },
       set_app_promotion_configuration_override: {
-        configuration: 'PalletConfigurationAppPromotionConfiguration'
+        configuration: 'PalletConfigurationAppPromotionConfiguration',
+      },
+      set_collator_selection_desired_collators: {
+        max: 'Option<u32>',
+      },
+      set_collator_selection_license_bond: {
+        amount: 'Option<u128>',
+      },
+      set_collator_selection_kick_threshold: {
+        threshold: 'Option<u32>'
       }
     }
   },
   /**
-   * Lookup284: pallet_configuration::AppPromotionConfiguration<BlockNumber>
+   * Lookup285: pallet_configuration::AppPromotionConfiguration<BlockNumber>
    **/
   PalletConfigurationAppPromotionConfiguration: {
     recalculationInterval: 'Option<u32>',
@@ -2469,15 +2494,15 @@
     maxStakersPerCalculation: 'Option<u8>'
   },
   /**
-   * Lookup288: pallet_template_transaction_payment::Call<T>
+   * Lookup289: pallet_template_transaction_payment::Call<T>
    **/
   PalletTemplateTransactionPaymentCall: 'Null',
   /**
-   * Lookup289: pallet_structure::pallet::Call<T>
+   * Lookup290: pallet_structure::pallet::Call<T>
    **/
   PalletStructureCall: 'Null',
   /**
-   * Lookup290: pallet_rmrk_core::pallet::Call<T>
+   * Lookup291: pallet_rmrk_core::pallet::Call<T>
    **/
   PalletRmrkCoreCall: {
     _enum: {
@@ -2568,7 +2593,7 @@
     }
   },
   /**
-   * Lookup296: rmrk_traits::resource::ResourceTypes<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup297: rmrk_traits::resource::ResourceTypes<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsResourceResourceTypes: {
     _enum: {
@@ -2578,7 +2603,7 @@
     }
   },
   /**
-   * Lookup298: rmrk_traits::resource::BasicResource<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup299: rmrk_traits::resource::BasicResource<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsResourceBasicResource: {
     src: 'Option<Bytes>',
@@ -2587,7 +2612,7 @@
     thumb: 'Option<Bytes>'
   },
   /**
-   * Lookup300: rmrk_traits::resource::ComposableResource<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup301: rmrk_traits::resource::ComposableResource<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsResourceComposableResource: {
     parts: 'Vec<u32>',
@@ -2598,7 +2623,7 @@
     thumb: 'Option<Bytes>'
   },
   /**
-   * Lookup301: rmrk_traits::resource::SlotResource<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup302: rmrk_traits::resource::SlotResource<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsResourceSlotResource: {
     base: 'u32',
@@ -2609,7 +2634,7 @@
     thumb: 'Option<Bytes>'
   },
   /**
-   * Lookup304: pallet_rmrk_equip::pallet::Call<T>
+   * Lookup305: pallet_rmrk_equip::pallet::Call<T>
    **/
   PalletRmrkEquipCall: {
     _enum: {
@@ -2630,7 +2655,7 @@
     }
   },
   /**
-   * Lookup307: rmrk_traits::part::PartType<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup308: rmrk_traits::part::PartType<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsPartPartType: {
     _enum: {
@@ -2639,7 +2664,7 @@
     }
   },
   /**
-   * Lookup309: rmrk_traits::part::FixedPart<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup310: rmrk_traits::part::FixedPart<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsPartFixedPart: {
     id: 'u32',
@@ -2647,7 +2672,7 @@
     src: 'Bytes'
   },
   /**
-   * Lookup310: rmrk_traits::part::SlotPart<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup311: rmrk_traits::part::SlotPart<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsPartSlotPart: {
     id: 'u32',
@@ -2656,7 +2681,7 @@
     z: 'u32'
   },
   /**
-   * Lookup311: rmrk_traits::part::EquippableList<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup312: rmrk_traits::part::EquippableList<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsPartEquippableList: {
     _enum: {
@@ -2666,7 +2691,7 @@
     }
   },
   /**
-   * Lookup313: rmrk_traits::theme::Theme<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<rmrk_traits::theme::ThemeProperty<sp_core::bounded::bounded_vec::BoundedVec<T, S>>, S>>
+   * Lookup314: rmrk_traits::theme::Theme<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<rmrk_traits::theme::ThemeProperty<sp_core::bounded::bounded_vec::BoundedVec<T, S>>, S>>
    **/
   RmrkTraitsTheme: {
     name: 'Bytes',
@@ -2674,14 +2699,14 @@
     inherit: 'bool'
   },
   /**
-   * Lookup315: rmrk_traits::theme::ThemeProperty<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup316: rmrk_traits::theme::ThemeProperty<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsThemeThemeProperty: {
     key: 'Bytes',
     value: 'Bytes'
   },
   /**
-   * Lookup317: pallet_app_promotion::pallet::Call<T>
+   * Lookup318: pallet_app_promotion::pallet::Call<T>
    **/
   PalletAppPromotionCall: {
     _enum: {
@@ -2710,7 +2735,7 @@
     }
   },
   /**
-   * Lookup318: pallet_foreign_assets::module::Call<T>
+   * Lookup319: pallet_foreign_assets::module::Call<T>
    **/
   PalletForeignAssetsModuleCall: {
     _enum: {
@@ -2727,7 +2752,7 @@
     }
   },
   /**
-   * Lookup319: pallet_evm::pallet::Call<T>
+   * Lookup320: pallet_evm::pallet::Call<T>
    **/
   PalletEvmCall: {
     _enum: {
@@ -2770,7 +2795,7 @@
     }
   },
   /**
-   * Lookup325: pallet_ethereum::pallet::Call<T>
+   * Lookup326: pallet_ethereum::pallet::Call<T>
    **/
   PalletEthereumCall: {
     _enum: {
@@ -2780,7 +2805,7 @@
     }
   },
   /**
-   * Lookup326: ethereum::transaction::TransactionV2
+   * Lookup327: ethereum::transaction::TransactionV2
    **/
   EthereumTransactionTransactionV2: {
     _enum: {
@@ -2790,7 +2815,7 @@
     }
   },
   /**
-   * Lookup327: ethereum::transaction::LegacyTransaction
+   * Lookup328: ethereum::transaction::LegacyTransaction
    **/
   EthereumTransactionLegacyTransaction: {
     nonce: 'U256',
@@ -2802,7 +2827,7 @@
     signature: 'EthereumTransactionTransactionSignature'
   },
   /**
-   * Lookup328: ethereum::transaction::TransactionAction
+   * Lookup329: ethereum::transaction::TransactionAction
    **/
   EthereumTransactionTransactionAction: {
     _enum: {
@@ -2811,7 +2836,7 @@
     }
   },
   /**
-   * Lookup329: ethereum::transaction::TransactionSignature
+   * Lookup330: ethereum::transaction::TransactionSignature
    **/
   EthereumTransactionTransactionSignature: {
     v: 'u64',
@@ -2819,7 +2844,7 @@
     s: 'H256'
   },
   /**
-   * Lookup331: ethereum::transaction::EIP2930Transaction
+   * Lookup332: ethereum::transaction::EIP2930Transaction
    **/
   EthereumTransactionEip2930Transaction: {
     chainId: 'u64',
@@ -2835,14 +2860,14 @@
     s: 'H256'
   },
   /**
-   * Lookup333: ethereum::transaction::AccessListItem
+   * Lookup334: ethereum::transaction::AccessListItem
    **/
   EthereumTransactionAccessListItem: {
     address: 'H160',
     storageKeys: 'Vec<H256>'
   },
   /**
-   * Lookup334: ethereum::transaction::EIP1559Transaction
+   * Lookup335: ethereum::transaction::EIP1559Transaction
    **/
   EthereumTransactionEip1559Transaction: {
     chainId: 'u64',
@@ -2859,7 +2884,7 @@
     s: 'H256'
   },
   /**
-   * Lookup335: pallet_evm_migration::pallet::Call<T>
+   * Lookup336: pallet_evm_migration::pallet::Call<T>
    **/
   PalletEvmMigrationCall: {
     _enum: {
@@ -2883,13 +2908,13 @@
     }
   },
   /**
-   * Lookup339: pallet_maintenance::pallet::Call<T>
+   * Lookup340: pallet_maintenance::pallet::Call<T>
    **/
   PalletMaintenanceCall: {
     _enum: ['enable', 'disable']
   },
   /**
-   * Lookup340: pallet_test_utils::pallet::Call<T>
+   * Lookup341: pallet_test_utils::pallet::Call<T>
    **/
   PalletTestUtilsCall: {
     _enum: {
@@ -2908,32 +2933,32 @@
     }
   },
   /**
-   * Lookup342: pallet_sudo::pallet::Error<T>
+   * Lookup343: pallet_sudo::pallet::Error<T>
    **/
   PalletSudoError: {
     _enum: ['RequireSudo']
   },
   /**
-   * Lookup344: orml_vesting::module::Error<T>
+   * Lookup345: orml_vesting::module::Error<T>
    **/
   OrmlVestingModuleError: {
     _enum: ['ZeroVestingPeriod', 'ZeroVestingPeriodCount', 'InsufficientBalanceToLock', 'TooManyVestingSchedules', 'AmountLow', 'MaxVestingSchedulesExceeded']
   },
   /**
-   * Lookup345: orml_xtokens::module::Error<T>
+   * Lookup346: orml_xtokens::module::Error<T>
    **/
   OrmlXtokensModuleError: {
     _enum: ['AssetHasNoReserve', 'NotCrossChainTransfer', 'InvalidDest', 'NotCrossChainTransferableCurrency', 'UnweighableMessage', 'XcmExecutionFailed', 'CannotReanchor', 'InvalidAncestry', 'InvalidAsset', 'DestinationNotInvertible', 'BadVersion', 'DistinctReserveForAssetAndFee', 'ZeroFee', 'ZeroAmount', 'TooManyAssetsBeingSent', 'AssetIndexNonExistent', 'FeeNotEnough', 'NotSupportedMultiLocation', 'MinXcmFeeNotDefined']
   },
   /**
-   * Lookup348: orml_tokens::BalanceLock<Balance>
+   * Lookup349: orml_tokens::BalanceLock<Balance>
    **/
   OrmlTokensBalanceLock: {
     id: '[u8;8]',
     amount: 'u128'
   },
   /**
-   * Lookup350: orml_tokens::AccountData<Balance>
+   * Lookup351: orml_tokens::AccountData<Balance>
    **/
   OrmlTokensAccountData: {
     free: 'u128',
@@ -2941,20 +2966,20 @@
     frozen: 'u128'
   },
   /**
-   * Lookup352: orml_tokens::ReserveData<ReserveIdentifier, Balance>
+   * Lookup353: orml_tokens::ReserveData<ReserveIdentifier, Balance>
    **/
   OrmlTokensReserveData: {
     id: 'Null',
     amount: 'u128'
   },
   /**
-   * Lookup354: orml_tokens::module::Error<T>
+   * Lookup355: orml_tokens::module::Error<T>
    **/
   OrmlTokensModuleError: {
     _enum: ['BalanceTooLow', 'AmountIntoBalanceFailed', 'LiquidityRestrictions', 'MaxLocksExceeded', 'KeepAlive', 'ExistentialDeposit', 'DeadAccount', 'TooManyReserves']
   },
   /**
-   * Lookup356: cumulus_pallet_xcmp_queue::InboundChannelDetails
+   * Lookup357: cumulus_pallet_xcmp_queue::InboundChannelDetails
    **/
   CumulusPalletXcmpQueueInboundChannelDetails: {
     sender: 'u32',
@@ -2962,19 +2987,19 @@
     messageMetadata: 'Vec<(u32,PolkadotParachainPrimitivesXcmpMessageFormat)>'
   },
   /**
-   * Lookup357: cumulus_pallet_xcmp_queue::InboundState
+   * Lookup358: cumulus_pallet_xcmp_queue::InboundState
    **/
   CumulusPalletXcmpQueueInboundState: {
     _enum: ['Ok', 'Suspended']
   },
   /**
-   * Lookup360: polkadot_parachain::primitives::XcmpMessageFormat
+   * Lookup361: polkadot_parachain::primitives::XcmpMessageFormat
    **/
   PolkadotParachainPrimitivesXcmpMessageFormat: {
     _enum: ['ConcatenatedVersionedXcm', 'ConcatenatedEncodedBlob', 'Signals']
   },
   /**
-   * Lookup363: cumulus_pallet_xcmp_queue::OutboundChannelDetails
+   * Lookup364: cumulus_pallet_xcmp_queue::OutboundChannelDetails
    **/
   CumulusPalletXcmpQueueOutboundChannelDetails: {
     recipient: 'u32',
@@ -2984,13 +3009,13 @@
     lastIndex: 'u16'
   },
   /**
-   * Lookup364: cumulus_pallet_xcmp_queue::OutboundState
+   * Lookup365: cumulus_pallet_xcmp_queue::OutboundState
    **/
   CumulusPalletXcmpQueueOutboundState: {
     _enum: ['Ok', 'Suspended']
   },
   /**
-   * Lookup366: cumulus_pallet_xcmp_queue::QueueConfigData
+   * Lookup367: cumulus_pallet_xcmp_queue::QueueConfigData
    **/
   CumulusPalletXcmpQueueQueueConfigData: {
     suspendThreshold: 'u32',
@@ -3001,29 +3026,29 @@
     xcmpMaxIndividualWeight: 'SpWeightsWeightV2Weight'
   },
   /**
-   * Lookup368: cumulus_pallet_xcmp_queue::pallet::Error<T>
+   * Lookup369: cumulus_pallet_xcmp_queue::pallet::Error<T>
    **/
   CumulusPalletXcmpQueueError: {
     _enum: ['FailedToSend', 'BadXcmOrigin', 'BadXcm', 'BadOverweightIndex', 'WeightOverLimit']
   },
   /**
-   * Lookup369: pallet_xcm::pallet::Error<T>
+   * Lookup370: pallet_xcm::pallet::Error<T>
    **/
   PalletXcmError: {
     _enum: ['Unreachable', 'SendFailure', 'Filtered', 'UnweighableMessage', 'DestinationNotInvertible', 'Empty', 'CannotReanchor', 'TooManyAssets', 'InvalidOrigin', 'BadVersion', 'BadLocation', 'NoSubscription', 'AlreadySubscribed']
   },
   /**
-   * Lookup370: cumulus_pallet_xcm::pallet::Error<T>
+   * Lookup371: cumulus_pallet_xcm::pallet::Error<T>
    **/
   CumulusPalletXcmError: 'Null',
   /**
-   * Lookup371: cumulus_pallet_dmp_queue::ConfigData
+   * Lookup372: cumulus_pallet_dmp_queue::ConfigData
    **/
   CumulusPalletDmpQueueConfigData: {
     maxIndividual: 'SpWeightsWeightV2Weight'
   },
   /**
-   * Lookup372: cumulus_pallet_dmp_queue::PageIndexData
+   * Lookup373: cumulus_pallet_dmp_queue::PageIndexData
    **/
   CumulusPalletDmpQueuePageIndexData: {
     beginUsed: 'u32',
@@ -3031,25 +3056,25 @@
     overweightCount: 'u64'
   },
   /**
-   * Lookup375: cumulus_pallet_dmp_queue::pallet::Error<T>
+   * Lookup376: cumulus_pallet_dmp_queue::pallet::Error<T>
    **/
   CumulusPalletDmpQueueError: {
     _enum: ['Unknown', 'OverLimit']
   },
   /**
-   * Lookup379: pallet_unique::Error<T>
+   * Lookup380: pallet_unique::Error<T>
    **/
   PalletUniqueError: {
     _enum: ['CollectionDecimalPointLimitExceeded', 'EmptyArgument', 'RepartitionCalledOnNonRefungibleCollection']
   },
   /**
-   * Lookup380: pallet_configuration::pallet::Error<T>
+   * Lookup381: pallet_configuration::pallet::Error<T>
    **/
   PalletConfigurationError: {
     _enum: ['InconsistentConfiguration']
   },
   /**
-   * Lookup381: up_data_structs::Collection<sp_core::crypto::AccountId32>
+   * Lookup382: up_data_structs::Collection<sp_core::crypto::AccountId32>
    **/
   UpDataStructsCollection: {
     owner: 'AccountId32',
@@ -3063,7 +3088,7 @@
     flags: '[u8;1]'
   },
   /**
-   * Lookup382: up_data_structs::SponsorshipState<sp_core::crypto::AccountId32>
+   * Lookup383: up_data_structs::SponsorshipState<sp_core::crypto::AccountId32>
    **/
   UpDataStructsSponsorshipStateAccountId32: {
     _enum: {
@@ -3073,7 +3098,7 @@
     }
   },
   /**
-   * Lookup384: up_data_structs::Properties
+   * Lookup385: up_data_structs::Properties
    **/
   UpDataStructsProperties: {
     map: 'UpDataStructsPropertiesMapBoundedVec',
@@ -3081,15 +3106,15 @@
     spaceLimit: 'u32'
   },
   /**
-   * Lookup385: up_data_structs::PropertiesMap<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup386: up_data_structs::PropertiesMap<sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   UpDataStructsPropertiesMapBoundedVec: 'BTreeMap<Bytes, Bytes>',
   /**
-   * Lookup390: up_data_structs::PropertiesMap<up_data_structs::PropertyPermission>
+   * Lookup391: up_data_structs::PropertiesMap<up_data_structs::PropertyPermission>
    **/
   UpDataStructsPropertiesMapPropertyPermission: 'BTreeMap<Bytes, UpDataStructsPropertyPermission>',
   /**
-   * Lookup397: up_data_structs::CollectionStats
+   * Lookup398: up_data_structs::CollectionStats
    **/
   UpDataStructsCollectionStats: {
     created: 'u32',
@@ -3097,18 +3122,18 @@
     alive: 'u32'
   },
   /**
-   * Lookup398: up_data_structs::TokenChild
+   * Lookup399: up_data_structs::TokenChild
    **/
   UpDataStructsTokenChild: {
     token: 'u32',
     collection: 'u32'
   },
   /**
-   * Lookup399: PhantomType::up_data_structs<T>
+   * Lookup400: PhantomType::up_data_structs<T>
    **/
   PhantomTypeUpDataStructs: '[(UpDataStructsTokenData,UpDataStructsRpcCollection,RmrkTraitsCollectionCollectionInfo,RmrkTraitsNftNftInfo,RmrkTraitsResourceResourceInfo,RmrkTraitsPropertyPropertyInfo,RmrkTraitsBaseBaseInfo,RmrkTraitsPartPartType,RmrkTraitsTheme,RmrkTraitsNftNftChild,UpPovEstimateRpcPovInfo);0]',
   /**
-   * Lookup401: up_data_structs::TokenData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   * Lookup402: up_data_structs::TokenData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
    **/
   UpDataStructsTokenData: {
     properties: 'Vec<UpDataStructsProperty>',
@@ -3116,7 +3141,7 @@
     pieces: 'u128'
   },
   /**
-   * Lookup403: up_data_structs::RpcCollection<sp_core::crypto::AccountId32>
+   * Lookup404: up_data_structs::RpcCollection<sp_core::crypto::AccountId32>
    **/
   UpDataStructsRpcCollection: {
     owner: 'AccountId32',
@@ -3133,14 +3158,14 @@
     flags: 'UpDataStructsRpcCollectionFlags'
   },
   /**
-   * Lookup404: up_data_structs::RpcCollectionFlags
+   * Lookup405: up_data_structs::RpcCollectionFlags
    **/
   UpDataStructsRpcCollectionFlags: {
     foreign: 'bool',
     erc721metadata: 'bool'
   },
   /**
-   * Lookup405: rmrk_traits::collection::CollectionInfo<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::crypto::AccountId32>
+   * Lookup406: rmrk_traits::collection::CollectionInfo<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::crypto::AccountId32>
    **/
   RmrkTraitsCollectionCollectionInfo: {
     issuer: 'AccountId32',
@@ -3150,7 +3175,7 @@
     nftsCount: 'u32'
   },
   /**
-   * Lookup406: rmrk_traits::nft::NftInfo<sp_core::crypto::AccountId32, sp_arithmetic::per_things::Permill, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup407: rmrk_traits::nft::NftInfo<sp_core::crypto::AccountId32, sp_arithmetic::per_things::Permill, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsNftNftInfo: {
     owner: 'RmrkTraitsNftAccountIdOrCollectionNftTuple',
@@ -3160,14 +3185,14 @@
     pending: 'bool'
   },
   /**
-   * Lookup408: rmrk_traits::nft::RoyaltyInfo<sp_core::crypto::AccountId32, sp_arithmetic::per_things::Permill>
+   * Lookup409: rmrk_traits::nft::RoyaltyInfo<sp_core::crypto::AccountId32, sp_arithmetic::per_things::Permill>
    **/
   RmrkTraitsNftRoyaltyInfo: {
     recipient: 'AccountId32',
     amount: 'Permill'
   },
   /**
-   * Lookup409: rmrk_traits::resource::ResourceInfo<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup410: rmrk_traits::resource::ResourceInfo<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsResourceResourceInfo: {
     id: 'u32',
@@ -3176,14 +3201,14 @@
     pendingRemoval: 'bool'
   },
   /**
-   * Lookup410: rmrk_traits::property::PropertyInfo<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup411: rmrk_traits::property::PropertyInfo<sp_core::bounded::bounded_vec::BoundedVec<T, S>, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsPropertyPropertyInfo: {
     key: 'Bytes',
     value: 'Bytes'
   },
   /**
-   * Lookup411: rmrk_traits::base::BaseInfo<sp_core::crypto::AccountId32, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup412: rmrk_traits::base::BaseInfo<sp_core::crypto::AccountId32, sp_core::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsBaseBaseInfo: {
     issuer: 'AccountId32',
@@ -3191,14 +3216,14 @@
     symbol: 'Bytes'
   },
   /**
-   * Lookup412: rmrk_traits::nft::NftChild
+   * Lookup413: rmrk_traits::nft::NftChild
    **/
   RmrkTraitsNftNftChild: {
     collectionId: 'u32',
     nftId: 'u32'
   },
   /**
-   * Lookup413: up_pov_estimate_rpc::PovInfo
+   * Lookup414: up_pov_estimate_rpc::PovInfo
    **/
   UpPovEstimateRpcPovInfo: {
     proofSize: 'u64',
@@ -3208,7 +3233,7 @@
     keyValues: 'Vec<UpPovEstimateRpcTrieKeyValue>'
   },
   /**
-   * Lookup416: sp_runtime::transaction_validity::TransactionValidityError
+   * Lookup417: sp_runtime::transaction_validity::TransactionValidityError
    **/
   SpRuntimeTransactionValidityTransactionValidityError: {
     _enum: {
@@ -3217,7 +3242,7 @@
     }
   },
   /**
-   * Lookup417: sp_runtime::transaction_validity::InvalidTransaction
+   * Lookup418: sp_runtime::transaction_validity::InvalidTransaction
    **/
   SpRuntimeTransactionValidityInvalidTransaction: {
     _enum: {
@@ -3235,7 +3260,7 @@
     }
   },
   /**
-   * Lookup418: sp_runtime::transaction_validity::UnknownTransaction
+   * Lookup419: sp_runtime::transaction_validity::UnknownTransaction
    **/
   SpRuntimeTransactionValidityUnknownTransaction: {
     _enum: {
@@ -3245,86 +3270,86 @@
     }
   },
   /**
-   * Lookup420: up_pov_estimate_rpc::TrieKeyValue
+   * Lookup421: up_pov_estimate_rpc::TrieKeyValue
    **/
   UpPovEstimateRpcTrieKeyValue: {
     key: 'Bytes',
     value: 'Bytes'
   },
   /**
-   * Lookup422: pallet_common::pallet::Error<T>
+   * Lookup423: pallet_common::pallet::Error<T>
    **/
   PalletCommonError: {
     _enum: ['CollectionNotFound', 'MustBeTokenOwner', 'NoPermission', 'CantDestroyNotEmptyCollection', 'PublicMintingNotAllowed', 'AddressNotInAllowlist', 'CollectionNameLimitExceeded', 'CollectionDescriptionLimitExceeded', 'CollectionTokenPrefixLimitExceeded', 'TotalCollectionsLimitExceeded', 'CollectionAdminCountExceeded', 'CollectionLimitBoundsExceeded', 'OwnerPermissionsCantBeReverted', 'TransferNotAllowed', 'AccountTokenLimitExceeded', 'CollectionTokenLimitExceeded', 'MetadataFlagFrozen', 'TokenNotFound', 'TokenValueTooLow', 'ApprovedValueTooLow', 'CantApproveMoreThanOwned', 'AddressIsZero', 'UnsupportedOperation', 'NotSufficientFounds', 'UserIsNotAllowedToNest', 'SourceCollectionIsNotAllowedToNest', 'CollectionFieldSizeExceeded', 'NoSpaceForProperty', 'PropertyLimitReached', 'PropertyKeyIsTooLong', 'InvalidCharacterInPropertyKey', 'EmptyPropertyKey', 'CollectionIsExternal', 'CollectionIsInternal', 'ConfirmSponsorshipFail', 'UserIsNotCollectionAdmin']
   },
   /**
-   * Lookup424: pallet_fungible::pallet::Error<T>
+   * Lookup425: pallet_fungible::pallet::Error<T>
    **/
   PalletFungibleError: {
     _enum: ['NotFungibleDataUsedToMintFungibleCollectionToken', 'FungibleItemsHaveNoId', 'FungibleItemsDontHaveData', 'FungibleDisallowsNesting', 'SettingPropertiesNotAllowed', 'SettingAllowanceForAllNotAllowed', 'FungibleTokensAreAlwaysValid']
   },
   /**
-   * Lookup428: pallet_refungible::pallet::Error<T>
+   * Lookup429: pallet_refungible::pallet::Error<T>
    **/
   PalletRefungibleError: {
     _enum: ['NotRefungibleDataUsedToMintFungibleCollectionToken', 'WrongRefungiblePieces', 'RepartitionWhileNotOwningAllPieces', 'RefungibleDisallowsNesting', 'SettingPropertiesNotAllowed']
   },
   /**
-   * Lookup429: pallet_nonfungible::ItemData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   * Lookup430: pallet_nonfungible::ItemData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
    **/
   PalletNonfungibleItemData: {
     owner: 'PalletEvmAccountBasicCrossAccountIdRepr'
   },
   /**
-   * Lookup431: up_data_structs::PropertyScope
+   * Lookup432: up_data_structs::PropertyScope
    **/
   UpDataStructsPropertyScope: {
     _enum: ['None', 'Rmrk']
   },
   /**
-   * Lookup434: pallet_nonfungible::pallet::Error<T>
+   * Lookup435: pallet_nonfungible::pallet::Error<T>
    **/
   PalletNonfungibleError: {
     _enum: ['NotNonfungibleDataUsedToMintFungibleCollectionToken', 'NonfungibleItemsHaveNoAmount', 'CantBurnNftWithChildren']
   },
   /**
-   * Lookup435: pallet_structure::pallet::Error<T>
+   * Lookup436: pallet_structure::pallet::Error<T>
    **/
   PalletStructureError: {
     _enum: ['OuroborosDetected', 'DepthLimit', 'BreadthLimit', 'TokenNotFound']
   },
   /**
-   * Lookup436: pallet_rmrk_core::pallet::Error<T>
+   * Lookup437: pallet_rmrk_core::pallet::Error<T>
    **/
   PalletRmrkCoreError: {
     _enum: ['CorruptedCollectionType', 'RmrkPropertyKeyIsTooLong', 'RmrkPropertyValueIsTooLong', 'RmrkPropertyIsNotFound', 'UnableToDecodeRmrkData', 'CollectionNotEmpty', 'NoAvailableCollectionId', 'NoAvailableNftId', 'CollectionUnknown', 'NoPermission', 'NonTransferable', 'CollectionFullOrLocked', 'ResourceDoesntExist', 'CannotSendToDescendentOrSelf', 'CannotAcceptNonOwnedNft', 'CannotRejectNonOwnedNft', 'CannotRejectNonPendingNft', 'ResourceNotPending', 'NoAvailableResourceId']
   },
   /**
-   * Lookup438: pallet_rmrk_equip::pallet::Error<T>
+   * Lookup439: pallet_rmrk_equip::pallet::Error<T>
    **/
   PalletRmrkEquipError: {
     _enum: ['PermissionError', 'NoAvailableBaseId', 'NoAvailablePartId', 'BaseDoesntExist', 'NeedsDefaultThemeFirst', 'PartDoesntExist', 'NoEquippableOnFixedPart']
   },
   /**
-   * Lookup444: pallet_app_promotion::pallet::Error<T>
+   * Lookup445: pallet_app_promotion::pallet::Error<T>
    **/
   PalletAppPromotionError: {
     _enum: ['AdminNotSet', 'NoPermission', 'NotSufficientFunds', 'PendingForBlockOverflow', 'SponsorNotSet', 'IncorrectLockedBalanceOperation']
   },
   /**
-   * Lookup445: pallet_foreign_assets::module::Error<T>
+   * Lookup446: pallet_foreign_assets::module::Error<T>
    **/
   PalletForeignAssetsModuleError: {
     _enum: ['BadLocation', 'MultiLocationExisted', 'AssetIdNotExists', 'AssetIdExisted']
   },
   /**
-   * Lookup447: pallet_evm::pallet::Error<T>
+   * Lookup448: pallet_evm::pallet::Error<T>
    **/
   PalletEvmError: {
     _enum: ['BalanceLow', 'FeeOverflow', 'PaymentOverflow', 'WithdrawFailed', 'GasPriceTooLow', 'InvalidNonce', 'GasLimitTooLow', 'GasLimitTooHigh', 'Undefined', 'Reentrancy', 'TransactionMustComeFromEOA']
   },
   /**
-   * Lookup450: fp_rpc::TransactionStatus
+   * Lookup451: fp_rpc::TransactionStatus
    **/
   FpRpcTransactionStatus: {
     transactionHash: 'H256',
@@ -3336,11 +3361,11 @@
     logsBloom: 'EthbloomBloom'
   },
   /**
-   * Lookup452: ethbloom::Bloom
+   * Lookup453: ethbloom::Bloom
    **/
   EthbloomBloom: '[u8;256]',
   /**
-   * Lookup454: ethereum::receipt::ReceiptV3
+   * Lookup455: ethereum::receipt::ReceiptV3
    **/
   EthereumReceiptReceiptV3: {
     _enum: {
@@ -3350,7 +3375,7 @@
     }
   },
   /**
-   * Lookup455: ethereum::receipt::EIP658ReceiptData
+   * Lookup456: ethereum::receipt::EIP658ReceiptData
    **/
   EthereumReceiptEip658ReceiptData: {
     statusCode: 'u8',
@@ -3359,7 +3384,7 @@
     logs: 'Vec<EthereumLog>'
   },
   /**
-   * Lookup456: ethereum::block::Block<ethereum::transaction::TransactionV2>
+   * Lookup457: ethereum::block::Block<ethereum::transaction::TransactionV2>
    **/
   EthereumBlock: {
     header: 'EthereumHeader',
@@ -3367,7 +3392,7 @@
     ommers: 'Vec<EthereumHeader>'
   },
   /**
-   * Lookup457: ethereum::header::Header
+   * Lookup458: ethereum::header::Header
    **/
   EthereumHeader: {
     parentHash: 'H256',
@@ -3387,23 +3412,23 @@
     nonce: 'EthereumTypesHashH64'
   },
   /**
-   * Lookup458: ethereum_types::hash::H64
+   * Lookup459: ethereum_types::hash::H64
    **/
   EthereumTypesHashH64: '[u8;8]',
   /**
-   * Lookup463: pallet_ethereum::pallet::Error<T>
+   * Lookup464: pallet_ethereum::pallet::Error<T>
    **/
   PalletEthereumError: {
     _enum: ['InvalidSignature', 'PreLogExists']
   },
   /**
-   * Lookup464: pallet_evm_coder_substrate::pallet::Error<T>
+   * Lookup465: pallet_evm_coder_substrate::pallet::Error<T>
    **/
   PalletEvmCoderSubstrateError: {
     _enum: ['OutOfGas', 'OutOfFund']
   },
   /**
-   * Lookup465: up_data_structs::SponsorshipState<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   * Lookup466: up_data_structs::SponsorshipState<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
    **/
   UpDataStructsSponsorshipStateBasicCrossAccountIdRepr: {
     _enum: {
@@ -3413,35 +3438,35 @@
     }
   },
   /**
-   * Lookup466: pallet_evm_contract_helpers::SponsoringModeT
+   * Lookup467: pallet_evm_contract_helpers::SponsoringModeT
    **/
   PalletEvmContractHelpersSponsoringModeT: {
     _enum: ['Disabled', 'Allowlisted', 'Generous']
   },
   /**
-   * Lookup472: pallet_evm_contract_helpers::pallet::Error<T>
+   * Lookup473: pallet_evm_contract_helpers::pallet::Error<T>
    **/
   PalletEvmContractHelpersError: {
     _enum: ['NoPermission', 'NoPendingSponsor', 'TooManyMethodsHaveSponsoredLimit']
   },
   /**
-   * Lookup473: pallet_evm_migration::pallet::Error<T>
+   * Lookup474: pallet_evm_migration::pallet::Error<T>
    **/
   PalletEvmMigrationError: {
     _enum: ['AccountNotEmpty', 'AccountIsNotMigrating', 'BadEvent']
   },
   /**
-   * Lookup474: pallet_maintenance::pallet::Error<T>
+   * Lookup475: pallet_maintenance::pallet::Error<T>
    **/
   PalletMaintenanceError: 'Null',
   /**
-   * Lookup475: pallet_test_utils::pallet::Error<T>
+   * Lookup476: pallet_test_utils::pallet::Error<T>
    **/
   PalletTestUtilsError: {
     _enum: ['TestPalletDisabled', 'TriggerRollback']
   },
   /**
-   * Lookup477: sp_runtime::MultiSignature
+   * Lookup478: sp_runtime::MultiSignature
    **/
   SpRuntimeMultiSignature: {
     _enum: {
@@ -3451,51 +3476,55 @@
     }
   },
   /**
-   * Lookup478: sp_core::ed25519::Signature
+   * Lookup479: sp_core::ed25519::Signature
    **/
   SpCoreEd25519Signature: '[u8;64]',
   /**
-   * Lookup480: sp_core::sr25519::Signature
+   * Lookup481: sp_core::sr25519::Signature
    **/
   SpCoreSr25519Signature: '[u8;64]',
   /**
-   * Lookup481: sp_core::ecdsa::Signature
+   * Lookup482: sp_core::ecdsa::Signature
    **/
   SpCoreEcdsaSignature: '[u8;65]',
   /**
-   * Lookup484: frame_system::extensions::check_spec_version::CheckSpecVersion<T>
+   * Lookup485: frame_system::extensions::check_spec_version::CheckSpecVersion<T>
    **/
   FrameSystemExtensionsCheckSpecVersion: 'Null',
   /**
-   * Lookup485: frame_system::extensions::check_tx_version::CheckTxVersion<T>
+   * Lookup486: frame_system::extensions::check_tx_version::CheckTxVersion<T>
    **/
   FrameSystemExtensionsCheckTxVersion: 'Null',
   /**
-   * Lookup486: frame_system::extensions::check_genesis::CheckGenesis<T>
+   * Lookup487: frame_system::extensions::check_genesis::CheckGenesis<T>
    **/
   FrameSystemExtensionsCheckGenesis: 'Null',
   /**
-   * Lookup489: frame_system::extensions::check_nonce::CheckNonce<T>
+   * Lookup490: frame_system::extensions::check_nonce::CheckNonce<T>
    **/
   FrameSystemExtensionsCheckNonce: 'Compact<u32>',
   /**
-   * Lookup490: frame_system::extensions::check_weight::CheckWeight<T>
+   * Lookup491: frame_system::extensions::check_weight::CheckWeight<T>
    **/
   FrameSystemExtensionsCheckWeight: 'Null',
   /**
-   * Lookup491: opal_runtime::runtime_common::maintenance::CheckMaintenance
+   * Lookup492: opal_runtime::runtime_common::maintenance::CheckMaintenance
    **/
   OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance: 'Null',
   /**
-   * Lookup492: pallet_template_transaction_payment::ChargeTransactionPayment<opal_runtime::Runtime>
+   * Lookup493: opal_runtime::runtime_common::identity::DisableIdentityCalls
    **/
+  OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls: 'Null',
+  /**
+   * Lookup494: pallet_template_transaction_payment::ChargeTransactionPayment<opal_runtime::Runtime>
+   **/
   PalletTemplateTransactionPaymentChargeTransactionPayment: 'Compact<u128>',
   /**
-   * Lookup493: opal_runtime::Runtime
+   * Lookup495: opal_runtime::Runtime
    **/
   OpalRuntimeRuntime: 'Null',
   /**
-   * Lookup494: pallet_ethereum::FakeTransactionFinalizer<opal_runtime::Runtime>
+   * Lookup496: pallet_ethereum::FakeTransactionFinalizer<opal_runtime::Runtime>
    **/
   PalletEthereumFakeTransactionFinalizer: 'Null'
 };
modifiedtests/src/interfaces/registry.tsdiffbeforeafterboth
--- a/tests/src/interfaces/registry.ts
+++ b/tests/src/interfaces/registry.ts
@@ -5,7 +5,7 @@
 // this is required to allow for ambient/previous definitions
 import '@polkadot/types/types/registry';
 
-import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchDispatchClass, FrameSupportDispatchDispatchInfo, FrameSupportDispatchPays, FrameSupportDispatchPerDispatchClassU32, FrameSupportDispatchPerDispatchClassWeight, FrameSupportDispatchPerDispatchClassWeightsPerClass, FrameSupportPalletId, FrameSupportTokensMiscBalanceStatus, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckTxVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeRuntime, OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensModuleCall, OrmlTokensModuleError, OrmlTokensModuleEvent, OrmlTokensReserveData, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, OrmlXtokensModuleCall, OrmlXtokensModuleError, OrmlXtokensModuleEvent, PalletAppPromotionCall, PalletAppPromotionError, PalletAppPromotionEvent, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletConfigurationAppPromotionConfiguration, PalletConfigurationCall, PalletConfigurationError, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersEvent, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletEvmMigrationEvent, PalletForeignAssetsAssetIds, PalletForeignAssetsModuleAssetMetadata, PalletForeignAssetsModuleCall, PalletForeignAssetsModuleError, PalletForeignAssetsModuleEvent, PalletForeignAssetsNativeCurrency, PalletFungibleError, PalletInflationCall, PalletMaintenanceCall, PalletMaintenanceError, PalletMaintenanceEvent, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTestUtilsCall, PalletTestUtilsError, PalletTestUtilsEvent, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletXcmCall, PalletXcmError, PalletXcmEvent, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionValidityInvalidTransaction, SpRuntimeTransactionValidityTransactionValidityError, SpRuntimeTransactionValidityUnknownTransaction, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, SpWeightsWeightV2Weight, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsRpcCollectionFlags, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipStateAccountId32, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, UpDataStructsTokenData, UpPovEstimateRpcPovInfo, UpPovEstimateRpcTrieKeyValue, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAsset, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup';
+import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchDispatchClass, FrameSupportDispatchDispatchInfo, FrameSupportDispatchPays, FrameSupportDispatchPerDispatchClassU32, FrameSupportDispatchPerDispatchClassWeight, FrameSupportDispatchPerDispatchClassWeightsPerClass, FrameSupportPalletId, FrameSupportTokensMiscBalanceStatus, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckTxVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeRuntime, OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls, OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensModuleCall, OrmlTokensModuleError, OrmlTokensModuleEvent, OrmlTokensReserveData, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, OrmlXtokensModuleCall, OrmlXtokensModuleError, OrmlXtokensModuleEvent, PalletAppPromotionCall, PalletAppPromotionError, PalletAppPromotionEvent, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletConfigurationAppPromotionConfiguration, PalletConfigurationCall, PalletConfigurationError, PalletConfigurationEvent, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersEvent, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletEvmMigrationEvent, PalletForeignAssetsAssetIds, PalletForeignAssetsModuleAssetMetadata, PalletForeignAssetsModuleCall, PalletForeignAssetsModuleError, PalletForeignAssetsModuleEvent, PalletForeignAssetsNativeCurrency, PalletFungibleError, PalletInflationCall, PalletMaintenanceCall, PalletMaintenanceError, PalletMaintenanceEvent, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTestUtilsCall, PalletTestUtilsError, PalletTestUtilsEvent, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletXcmCall, PalletXcmError, PalletXcmEvent, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionValidityInvalidTransaction, SpRuntimeTransactionValidityTransactionValidityError, SpRuntimeTransactionValidityUnknownTransaction, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, SpWeightsWeightV2Weight, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsRpcCollectionFlags, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipStateAccountId32, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, UpDataStructsTokenData, UpPovEstimateRpcPovInfo, UpPovEstimateRpcTrieKeyValue, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAsset, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup';
 
 declare module '@polkadot/types/types/registry' {
   interface InterfaceTypes {
@@ -74,6 +74,7 @@
     FrameSystemLimitsWeightsPerClass: FrameSystemLimitsWeightsPerClass;
     FrameSystemPhase: FrameSystemPhase;
     OpalRuntimeRuntime: OpalRuntimeRuntime;
+    OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls: OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls;
     OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance: OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance;
     OrmlTokensAccountData: OrmlTokensAccountData;
     OrmlTokensBalanceLock: OrmlTokensBalanceLock;
@@ -103,6 +104,7 @@
     PalletConfigurationAppPromotionConfiguration: PalletConfigurationAppPromotionConfiguration;
     PalletConfigurationCall: PalletConfigurationCall;
     PalletConfigurationError: PalletConfigurationError;
+    PalletConfigurationEvent: PalletConfigurationEvent;
     PalletEthereumCall: PalletEthereumCall;
     PalletEthereumError: PalletEthereumError;
     PalletEthereumEvent: PalletEthereumEvent;
modifiedtests/src/interfaces/types-lookup.tsdiffbeforeafterboth
--- a/tests/src/interfaces/types-lookup.ts
+++ b/tests/src/interfaces/types-lookup.ts
@@ -1109,7 +1109,24 @@
     readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward' | 'WeightExhausted' | 'OverweightEnqueued' | 'OverweightServiced';
   }
 
-  /** @name PalletCommonEvent (89) */
+  /** @name PalletConfigurationEvent (89) */
+  interface PalletConfigurationEvent extends Enum {
+    readonly isNewDesiredCollators: boolean;
+    readonly asNewDesiredCollators: {
+      readonly desiredCollators: Option<u32>;
+    } & Struct;
+    readonly isNewCollatorLicenseBond: boolean;
+    readonly asNewCollatorLicenseBond: {
+      readonly bondCost: Option<u128>;
+    } & Struct;
+    readonly isNewCollatorKickThreshold: boolean;
+    readonly asNewCollatorKickThreshold: {
+      readonly lengthInBlocks: Option<u32>;
+    } & Struct;
+    readonly type: 'NewDesiredCollators' | 'NewCollatorLicenseBond' | 'NewCollatorKickThreshold';
+  }
+
+  /** @name PalletCommonEvent (92) */
   interface PalletCommonEvent extends Enum {
     readonly isCollectionCreated: boolean;
     readonly asCollectionCreated: ITuple<[u32, u8, AccountId32]>;
@@ -1158,7 +1175,7 @@
     readonly type: 'CollectionCreated' | 'CollectionDestroyed' | 'ItemCreated' | 'ItemDestroyed' | 'Transfer' | 'Approved' | 'ApprovedForAll' | 'CollectionPropertySet' | 'CollectionPropertyDeleted' | 'TokenPropertySet' | 'TokenPropertyDeleted' | 'PropertyPermissionSet' | 'AllowListAddressAdded' | 'AllowListAddressRemoved' | 'CollectionAdminAdded' | 'CollectionAdminRemoved' | 'CollectionLimitSet' | 'CollectionOwnerChanged' | 'CollectionPermissionSet' | 'CollectionSponsorSet' | 'SponsorshipConfirmed' | 'CollectionSponsorRemoved';
   }
 
-  /** @name PalletEvmAccountBasicCrossAccountIdRepr (92) */
+  /** @name PalletEvmAccountBasicCrossAccountIdRepr (95) */
   interface PalletEvmAccountBasicCrossAccountIdRepr extends Enum {
     readonly isSubstrate: boolean;
     readonly asSubstrate: AccountId32;
@@ -1167,14 +1184,14 @@
     readonly type: 'Substrate' | 'Ethereum';
   }
 
-  /** @name PalletStructureEvent (96) */
+  /** @name PalletStructureEvent (99) */
   interface PalletStructureEvent extends Enum {
     readonly isExecuted: boolean;
     readonly asExecuted: Result<Null, SpRuntimeDispatchError>;
     readonly type: 'Executed';
   }
 
-  /** @name PalletRmrkCoreEvent (97) */
+  /** @name PalletRmrkCoreEvent (100) */
   interface PalletRmrkCoreEvent extends Enum {
     readonly isCollectionCreated: boolean;
     readonly asCollectionCreated: {
@@ -1264,7 +1281,7 @@
     readonly type: 'CollectionCreated' | 'CollectionDestroyed' | 'IssuerChanged' | 'CollectionLocked' | 'NftMinted' | 'NftBurned' | 'NftSent' | 'NftAccepted' | 'NftRejected' | 'PropertySet' | 'ResourceAdded' | 'ResourceRemoval' | 'ResourceAccepted' | 'ResourceRemovalAccepted' | 'PrioritySet';
   }
 
-  /** @name RmrkTraitsNftAccountIdOrCollectionNftTuple (98) */
+  /** @name RmrkTraitsNftAccountIdOrCollectionNftTuple (101) */
   interface RmrkTraitsNftAccountIdOrCollectionNftTuple extends Enum {
     readonly isAccountId: boolean;
     readonly asAccountId: AccountId32;
@@ -1273,7 +1290,7 @@
     readonly type: 'AccountId' | 'CollectionAndNftTuple';
   }
 
-  /** @name PalletRmrkEquipEvent (102) */
+  /** @name PalletRmrkEquipEvent (104) */
   interface PalletRmrkEquipEvent extends Enum {
     readonly isBaseCreated: boolean;
     readonly asBaseCreated: {
@@ -1288,7 +1305,7 @@
     readonly type: 'BaseCreated' | 'EquippablesUpdated';
   }
 
-  /** @name PalletAppPromotionEvent (103) */
+  /** @name PalletAppPromotionEvent (105) */
   interface PalletAppPromotionEvent extends Enum {
     readonly isStakingRecalculation: boolean;
     readonly asStakingRecalculation: ITuple<[AccountId32, u128, u128]>;
@@ -1301,7 +1318,7 @@
     readonly type: 'StakingRecalculation' | 'Stake' | 'Unstake' | 'SetAdmin';
   }
 
-  /** @name PalletForeignAssetsModuleEvent (104) */
+  /** @name PalletForeignAssetsModuleEvent (106) */
   interface PalletForeignAssetsModuleEvent extends Enum {
     readonly isForeignAssetRegistered: boolean;
     readonly asForeignAssetRegistered: {
@@ -1328,7 +1345,7 @@
     readonly type: 'ForeignAssetRegistered' | 'ForeignAssetUpdated' | 'AssetRegistered' | 'AssetUpdated';
   }
 
-  /** @name PalletForeignAssetsModuleAssetMetadata (105) */
+  /** @name PalletForeignAssetsModuleAssetMetadata (107) */
   interface PalletForeignAssetsModuleAssetMetadata extends Struct {
     readonly name: Bytes;
     readonly symbol: Bytes;
@@ -1336,7 +1353,7 @@
     readonly minimalBalance: u128;
   }
 
-  /** @name PalletEvmEvent (106) */
+  /** @name PalletEvmEvent (108) */
   interface PalletEvmEvent extends Enum {
     readonly isLog: boolean;
     readonly asLog: {
@@ -1361,14 +1378,14 @@
     readonly type: 'Log' | 'Created' | 'CreatedFailed' | 'Executed' | 'ExecutedFailed';
   }
 
-  /** @name EthereumLog (107) */
+  /** @name EthereumLog (109) */
   interface EthereumLog extends Struct {
     readonly address: H160;
     readonly topics: Vec<H256>;
     readonly data: Bytes;
   }
 
-  /** @name PalletEthereumEvent (109) */
+  /** @name PalletEthereumEvent (111) */
   interface PalletEthereumEvent extends Enum {
     readonly isExecuted: boolean;
     readonly asExecuted: {
@@ -1380,7 +1397,7 @@
     readonly type: 'Executed';
   }
 
-  /** @name EvmCoreErrorExitReason (110) */
+  /** @name EvmCoreErrorExitReason (112) */
   interface EvmCoreErrorExitReason extends Enum {
     readonly isSucceed: boolean;
     readonly asSucceed: EvmCoreErrorExitSucceed;
@@ -1393,7 +1410,7 @@
     readonly type: 'Succeed' | 'Error' | 'Revert' | 'Fatal';
   }
 
-  /** @name EvmCoreErrorExitSucceed (111) */
+  /** @name EvmCoreErrorExitSucceed (113) */
   interface EvmCoreErrorExitSucceed extends Enum {
     readonly isStopped: boolean;
     readonly isReturned: boolean;
@@ -1401,7 +1418,7 @@
     readonly type: 'Stopped' | 'Returned' | 'Suicided';
   }
 
-  /** @name EvmCoreErrorExitError (112) */
+  /** @name EvmCoreErrorExitError (114) */
   interface EvmCoreErrorExitError extends Enum {
     readonly isStackUnderflow: boolean;
     readonly isStackOverflow: boolean;
@@ -1422,13 +1439,13 @@
     readonly type: 'StackUnderflow' | 'StackOverflow' | 'InvalidJump' | 'InvalidRange' | 'DesignatedInvalid' | 'CallTooDeep' | 'CreateCollision' | 'CreateContractLimit' | 'OutOfOffset' | 'OutOfGas' | 'OutOfFund' | 'PcUnderflow' | 'CreateEmpty' | 'Other' | 'InvalidCode';
   }
 
-  /** @name EvmCoreErrorExitRevert (115) */
+  /** @name EvmCoreErrorExitRevert (117) */
   interface EvmCoreErrorExitRevert extends Enum {
     readonly isReverted: boolean;
     readonly type: 'Reverted';
   }
 
-  /** @name EvmCoreErrorExitFatal (116) */
+  /** @name EvmCoreErrorExitFatal (118) */
   interface EvmCoreErrorExitFatal extends Enum {
     readonly isNotSupported: boolean;
     readonly isUnhandledInterrupt: boolean;
@@ -1439,7 +1456,7 @@
     readonly type: 'NotSupported' | 'UnhandledInterrupt' | 'CallErrorAsFatal' | 'Other';
   }
 
-  /** @name PalletEvmContractHelpersEvent (117) */
+  /** @name PalletEvmContractHelpersEvent (119) */
   interface PalletEvmContractHelpersEvent extends Enum {
     readonly isContractSponsorSet: boolean;
     readonly asContractSponsorSet: ITuple<[H160, AccountId32]>;
@@ -1450,20 +1467,20 @@
     readonly type: 'ContractSponsorSet' | 'ContractSponsorshipConfirmed' | 'ContractSponsorRemoved';
   }
 
-  /** @name PalletEvmMigrationEvent (118) */
+  /** @name PalletEvmMigrationEvent (120) */
   interface PalletEvmMigrationEvent extends Enum {
     readonly isTestEvent: boolean;
     readonly type: 'TestEvent';
   }
 
-  /** @name PalletMaintenanceEvent (119) */
+  /** @name PalletMaintenanceEvent (121) */
   interface PalletMaintenanceEvent extends Enum {
     readonly isMaintenanceEnabled: boolean;
     readonly isMaintenanceDisabled: boolean;
     readonly type: 'MaintenanceEnabled' | 'MaintenanceDisabled';
   }
 
-  /** @name PalletTestUtilsEvent (120) */
+  /** @name PalletTestUtilsEvent (122) */
   interface PalletTestUtilsEvent extends Enum {
     readonly isValueIsSet: boolean;
     readonly isShouldRollback: boolean;
@@ -1471,7 +1488,7 @@
     readonly type: 'ValueIsSet' | 'ShouldRollback' | 'BatchCompleted';
   }
 
-  /** @name FrameSystemPhase (121) */
+  /** @name FrameSystemPhase (123) */
   interface FrameSystemPhase extends Enum {
     readonly isApplyExtrinsic: boolean;
     readonly asApplyExtrinsic: u32;
@@ -1480,13 +1497,13 @@
     readonly type: 'ApplyExtrinsic' | 'Finalization' | 'Initialization';
   }
 
-  /** @name FrameSystemLastRuntimeUpgradeInfo (124) */
+  /** @name FrameSystemLastRuntimeUpgradeInfo (126) */
   interface FrameSystemLastRuntimeUpgradeInfo extends Struct {
     readonly specVersion: Compact<u32>;
     readonly specName: Text;
   }
 
-  /** @name FrameSystemCall (125) */
+  /** @name FrameSystemCall (127) */
   interface FrameSystemCall extends Enum {
     readonly isRemark: boolean;
     readonly asRemark: {
@@ -1524,21 +1541,21 @@
     readonly type: 'Remark' | 'SetHeapPages' | 'SetCode' | 'SetCodeWithoutChecks' | 'SetStorage' | 'KillStorage' | 'KillPrefix' | 'RemarkWithEvent';
   }
 
-  /** @name FrameSystemLimitsBlockWeights (129) */
+  /** @name FrameSystemLimitsBlockWeights (131) */
   interface FrameSystemLimitsBlockWeights extends Struct {
     readonly baseBlock: SpWeightsWeightV2Weight;
     readonly maxBlock: SpWeightsWeightV2Weight;
     readonly perClass: FrameSupportDispatchPerDispatchClassWeightsPerClass;
   }
 
-  /** @name FrameSupportDispatchPerDispatchClassWeightsPerClass (130) */
+  /** @name FrameSupportDispatchPerDispatchClassWeightsPerClass (132) */
   interface FrameSupportDispatchPerDispatchClassWeightsPerClass extends Struct {
     readonly normal: FrameSystemLimitsWeightsPerClass;
     readonly operational: FrameSystemLimitsWeightsPerClass;
     readonly mandatory: FrameSystemLimitsWeightsPerClass;
   }
 
-  /** @name FrameSystemLimitsWeightsPerClass (131) */
+  /** @name FrameSystemLimitsWeightsPerClass (133) */
   interface FrameSystemLimitsWeightsPerClass extends Struct {
     readonly baseExtrinsic: SpWeightsWeightV2Weight;
     readonly maxExtrinsic: Option<SpWeightsWeightV2Weight>;
@@ -1546,25 +1563,25 @@
     readonly reserved: Option<SpWeightsWeightV2Weight>;
   }
 
-  /** @name FrameSystemLimitsBlockLength (133) */
+  /** @name FrameSystemLimitsBlockLength (135) */
   interface FrameSystemLimitsBlockLength extends Struct {
     readonly max: FrameSupportDispatchPerDispatchClassU32;
   }
 
-  /** @name FrameSupportDispatchPerDispatchClassU32 (134) */
+  /** @name FrameSupportDispatchPerDispatchClassU32 (136) */
   interface FrameSupportDispatchPerDispatchClassU32 extends Struct {
     readonly normal: u32;
     readonly operational: u32;
     readonly mandatory: u32;
   }
 
-  /** @name SpWeightsRuntimeDbWeight (135) */
+  /** @name SpWeightsRuntimeDbWeight (137) */
   interface SpWeightsRuntimeDbWeight extends Struct {
     readonly read: u64;
     readonly write: u64;
   }
 
-  /** @name SpVersionRuntimeVersion (136) */
+  /** @name SpVersionRuntimeVersion (138) */
   interface SpVersionRuntimeVersion extends Struct {
     readonly specName: Text;
     readonly implName: Text;
@@ -1576,7 +1593,7 @@
     readonly stateVersion: u8;
   }
 
-  /** @name FrameSystemError (141) */
+  /** @name FrameSystemError (143) */
   interface FrameSystemError extends Enum {
     readonly isInvalidSpecName: boolean;
     readonly isSpecVersionNeedsToIncrease: boolean;
@@ -1587,7 +1604,7 @@
     readonly type: 'InvalidSpecName' | 'SpecVersionNeedsToIncrease' | 'FailedToExtractRuntimeVersion' | 'NonDefaultComposite' | 'NonZeroRefCount' | 'CallFiltered';
   }
 
-  /** @name PolkadotPrimitivesV2PersistedValidationData (142) */
+  /** @name PolkadotPrimitivesV2PersistedValidationData (144) */
   interface PolkadotPrimitivesV2PersistedValidationData extends Struct {
     readonly parentHead: Bytes;
     readonly relayParentNumber: u32;
@@ -1595,18 +1612,18 @@
     readonly maxPovSize: u32;
   }
 
-  /** @name PolkadotPrimitivesV2UpgradeRestriction (145) */
+  /** @name PolkadotPrimitivesV2UpgradeRestriction (147) */
   interface PolkadotPrimitivesV2UpgradeRestriction extends Enum {
     readonly isPresent: boolean;
     readonly type: 'Present';
   }
 
-  /** @name SpTrieStorageProof (146) */
+  /** @name SpTrieStorageProof (148) */
   interface SpTrieStorageProof extends Struct {
     readonly trieNodes: BTreeSet<Bytes>;
   }
 
-  /** @name CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot (148) */
+  /** @name CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot (150) */
   interface CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot extends Struct {
     readonly dmqMqcHead: H256;
     readonly relayDispatchQueueSize: ITuple<[u32, u32]>;
@@ -1614,7 +1631,7 @@
     readonly egressChannels: Vec<ITuple<[u32, PolkadotPrimitivesV2AbridgedHrmpChannel]>>;
   }
 
-  /** @name PolkadotPrimitivesV2AbridgedHrmpChannel (151) */
+  /** @name PolkadotPrimitivesV2AbridgedHrmpChannel (153) */
   interface PolkadotPrimitivesV2AbridgedHrmpChannel extends Struct {
     readonly maxCapacity: u32;
     readonly maxTotalSize: u32;
@@ -1624,7 +1641,7 @@
     readonly mqcHead: Option<H256>;
   }
 
-  /** @name PolkadotPrimitivesV2AbridgedHostConfiguration (152) */
+  /** @name PolkadotPrimitivesV2AbridgedHostConfiguration (154) */
   interface PolkadotPrimitivesV2AbridgedHostConfiguration extends Struct {
     readonly maxCodeSize: u32;
     readonly maxHeadDataSize: u32;
@@ -1637,13 +1654,13 @@
     readonly validationUpgradeDelay: u32;
   }
 
-  /** @name PolkadotCorePrimitivesOutboundHrmpMessage (158) */
+  /** @name PolkadotCorePrimitivesOutboundHrmpMessage (160) */
   interface PolkadotCorePrimitivesOutboundHrmpMessage extends Struct {
     readonly recipient: u32;
     readonly data: Bytes;
   }
 
-  /** @name CumulusPalletParachainSystemCall (159) */
+  /** @name CumulusPalletParachainSystemCall (161) */
   interface CumulusPalletParachainSystemCall extends Enum {
     readonly isSetValidationData: boolean;
     readonly asSetValidationData: {
@@ -1664,7 +1681,7 @@
     readonly type: 'SetValidationData' | 'SudoSendUpwardMessage' | 'AuthorizeUpgrade' | 'EnactAuthorizedUpgrade';
   }
 
-  /** @name CumulusPrimitivesParachainInherentParachainInherentData (160) */
+  /** @name CumulusPrimitivesParachainInherentParachainInherentData (162) */
   interface CumulusPrimitivesParachainInherentParachainInherentData extends Struct {
     readonly validationData: PolkadotPrimitivesV2PersistedValidationData;
     readonly relayChainState: SpTrieStorageProof;
@@ -1672,19 +1689,19 @@
     readonly horizontalMessages: BTreeMap<u32, Vec<PolkadotCorePrimitivesInboundHrmpMessage>>;
   }
 
-  /** @name PolkadotCorePrimitivesInboundDownwardMessage (162) */
+  /** @name PolkadotCorePrimitivesInboundDownwardMessage (164) */
   interface PolkadotCorePrimitivesInboundDownwardMessage extends Struct {
     readonly sentAt: u32;
     readonly msg: Bytes;
   }
 
-  /** @name PolkadotCorePrimitivesInboundHrmpMessage (165) */
+  /** @name PolkadotCorePrimitivesInboundHrmpMessage (167) */
   interface PolkadotCorePrimitivesInboundHrmpMessage extends Struct {
     readonly sentAt: u32;
     readonly data: Bytes;
   }
 
-  /** @name CumulusPalletParachainSystemError (168) */
+  /** @name CumulusPalletParachainSystemError (170) */
   interface CumulusPalletParachainSystemError extends Enum {
     readonly isOverlappingUpgrades: boolean;
     readonly isProhibitedByPolkadot: boolean;
@@ -1697,14 +1714,14 @@
     readonly type: 'OverlappingUpgrades' | 'ProhibitedByPolkadot' | 'TooBig' | 'ValidationDataNotAvailable' | 'HostConfigurationNotAvailable' | 'NotScheduled' | 'NothingAuthorized' | 'Unauthorized';
   }
 
-  /** @name PalletBalancesBalanceLock (170) */
+  /** @name PalletBalancesBalanceLock (172) */
   interface PalletBalancesBalanceLock extends Struct {
     readonly id: U8aFixed;
     readonly amount: u128;
     readonly reasons: PalletBalancesReasons;
   }
 
-  /** @name PalletBalancesReasons (171) */
+  /** @name PalletBalancesReasons (173) */
   interface PalletBalancesReasons extends Enum {
     readonly isFee: boolean;
     readonly isMisc: boolean;
@@ -1712,13 +1729,13 @@
     readonly type: 'Fee' | 'Misc' | 'All';
   }
 
-  /** @name PalletBalancesReserveData (174) */
+  /** @name PalletBalancesReserveData (176) */
   interface PalletBalancesReserveData extends Struct {
     readonly id: U8aFixed;
     readonly amount: u128;
   }
 
-  /** @name PalletBalancesCall (176) */
+  /** @name PalletBalancesCall (178) */
   interface PalletBalancesCall extends Enum {
     readonly isTransfer: boolean;
     readonly asTransfer: {
@@ -1755,7 +1772,7 @@
     readonly type: 'Transfer' | 'SetBalance' | 'ForceTransfer' | 'TransferKeepAlive' | 'TransferAll' | 'ForceUnreserve';
   }
 
-  /** @name PalletBalancesError (179) */
+  /** @name PalletBalancesError (181) */
   interface PalletBalancesError extends Enum {
     readonly isVestingBalance: boolean;
     readonly isLiquidityRestrictions: boolean;
@@ -1768,7 +1785,7 @@
     readonly type: 'VestingBalance' | 'LiquidityRestrictions' | 'InsufficientBalance' | 'ExistentialDeposit' | 'KeepAlive' | 'ExistingVestingSchedule' | 'DeadAccount' | 'TooManyReserves';
   }
 
-  /** @name PalletTimestampCall (181) */
+  /** @name PalletTimestampCall (183) */
   interface PalletTimestampCall extends Enum {
     readonly isSet: boolean;
     readonly asSet: {
@@ -1777,14 +1794,14 @@
     readonly type: 'Set';
   }
 
-  /** @name PalletTransactionPaymentReleases (183) */
+  /** @name PalletTransactionPaymentReleases (185) */
   interface PalletTransactionPaymentReleases extends Enum {
     readonly isV1Ancient: boolean;
     readonly isV2: boolean;
     readonly type: 'V1Ancient' | 'V2';
   }
 
-  /** @name PalletTreasuryProposal (184) */
+  /** @name PalletTreasuryProposal (186) */
   interface PalletTreasuryProposal extends Struct {
     readonly proposer: AccountId32;
     readonly value: u128;
@@ -1792,7 +1809,7 @@
     readonly bond: u128;
   }
 
-  /** @name PalletTreasuryCall (187) */
+  /** @name PalletTreasuryCall (189) */
   interface PalletTreasuryCall extends Enum {
     readonly isProposeSpend: boolean;
     readonly asProposeSpend: {
@@ -1819,10 +1836,10 @@
     readonly type: 'ProposeSpend' | 'RejectProposal' | 'ApproveProposal' | 'Spend' | 'RemoveApproval';
   }
 
-  /** @name FrameSupportPalletId (190) */
+  /** @name FrameSupportPalletId (191) */
   interface FrameSupportPalletId extends U8aFixed {}
 
-  /** @name PalletTreasuryError (191) */
+  /** @name PalletTreasuryError (192) */
   interface PalletTreasuryError extends Enum {
     readonly isInsufficientProposersBalance: boolean;
     readonly isInvalidIndex: boolean;
@@ -1832,7 +1849,7 @@
     readonly type: 'InsufficientProposersBalance' | 'InvalidIndex' | 'TooManyApprovals' | 'InsufficientPermission' | 'ProposalNotApproved';
   }
 
-  /** @name PalletSudoCall (192) */
+  /** @name PalletSudoCall (193) */
   interface PalletSudoCall extends Enum {
     readonly isSudo: boolean;
     readonly asSudo: {
@@ -1855,7 +1872,7 @@
     readonly type: 'Sudo' | 'SudoUncheckedWeight' | 'SetKey' | 'SudoAs';
   }
 
-  /** @name OrmlVestingModuleCall (194) */
+  /** @name OrmlVestingModuleCall (195) */
   interface OrmlVestingModuleCall extends Enum {
     readonly isClaim: boolean;
     readonly isVestedTransfer: boolean;
@@ -1875,7 +1892,7 @@
     readonly type: 'Claim' | 'VestedTransfer' | 'UpdateVestingSchedules' | 'ClaimFor';
   }
 
-  /** @name OrmlXtokensModuleCall (196) */
+  /** @name OrmlXtokensModuleCall (197) */
   interface OrmlXtokensModuleCall extends Enum {
     readonly isTransfer: boolean;
     readonly asTransfer: {
@@ -1922,7 +1939,7 @@
     readonly type: 'Transfer' | 'TransferMultiasset' | 'TransferWithFee' | 'TransferMultiassetWithFee' | 'TransferMulticurrencies' | 'TransferMultiassets';
   }
 
-  /** @name XcmVersionedMultiAsset (197) */
+  /** @name XcmVersionedMultiAsset (198) */
   interface XcmVersionedMultiAsset extends Enum {
     readonly isV0: boolean;
     readonly asV0: XcmV0MultiAsset;
@@ -1931,7 +1948,7 @@
     readonly type: 'V0' | 'V1';
   }
 
-  /** @name OrmlTokensModuleCall (200) */
+  /** @name OrmlTokensModuleCall (201) */
   interface OrmlTokensModuleCall extends Enum {
     readonly isTransfer: boolean;
     readonly asTransfer: {
@@ -1968,7 +1985,7 @@
     readonly type: 'Transfer' | 'TransferAll' | 'TransferKeepAlive' | 'ForceTransfer' | 'SetBalance';
   }
 
-  /** @name CumulusPalletXcmpQueueCall (201) */
+  /** @name CumulusPalletXcmpQueueCall (202) */
   interface CumulusPalletXcmpQueueCall extends Enum {
     readonly isServiceOverweight: boolean;
     readonly asServiceOverweight: {
@@ -2004,7 +2021,7 @@
     readonly type: 'ServiceOverweight' | 'SuspendXcmExecution' | 'ResumeXcmExecution' | 'UpdateSuspendThreshold' | 'UpdateDropThreshold' | 'UpdateResumeThreshold' | 'UpdateThresholdWeight' | 'UpdateWeightRestrictDecay' | 'UpdateXcmpMaxIndividualWeight';
   }
 
-  /** @name PalletXcmCall (202) */
+  /** @name PalletXcmCall (203) */
   interface PalletXcmCall extends Enum {
     readonly isSend: boolean;
     readonly asSend: {
@@ -2066,7 +2083,7 @@
     readonly type: 'Send' | 'TeleportAssets' | 'ReserveTransferAssets' | 'Execute' | 'ForceXcmVersion' | 'ForceDefaultXcmVersion' | 'ForceSubscribeVersionNotify' | 'ForceUnsubscribeVersionNotify' | 'LimitedReserveTransferAssets' | 'LimitedTeleportAssets';
   }
 
-  /** @name XcmVersionedXcm (203) */
+  /** @name XcmVersionedXcm (204) */
   interface XcmVersionedXcm extends Enum {
     readonly isV0: boolean;
     readonly asV0: XcmV0Xcm;
@@ -2077,7 +2094,7 @@
     readonly type: 'V0' | 'V1' | 'V2';
   }
 
-  /** @name XcmV0Xcm (204) */
+  /** @name XcmV0Xcm (205) */
   interface XcmV0Xcm extends Enum {
     readonly isWithdrawAsset: boolean;
     readonly asWithdrawAsset: {
@@ -2140,7 +2157,7 @@
     readonly type: 'WithdrawAsset' | 'ReserveAssetDeposit' | 'TeleportAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'RelayedFrom';
   }
 
-  /** @name XcmV0Order (206) */
+  /** @name XcmV0Order (207) */
   interface XcmV0Order extends Enum {
     readonly isNull: boolean;
     readonly isDepositAsset: boolean;
@@ -2188,14 +2205,14 @@
     readonly type: 'Null' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'QueryHolding' | 'BuyExecution';
   }
 
-  /** @name XcmV0Response (208) */
+  /** @name XcmV0Response (209) */
   interface XcmV0Response extends Enum {
     readonly isAssets: boolean;
     readonly asAssets: Vec<XcmV0MultiAsset>;
     readonly type: 'Assets';
   }
 
-  /** @name XcmV1Xcm (209) */
+  /** @name XcmV1Xcm (210) */
   interface XcmV1Xcm extends Enum {
     readonly isWithdrawAsset: boolean;
     readonly asWithdrawAsset: {
@@ -2264,7 +2281,7 @@
     readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'RelayedFrom' | 'SubscribeVersion' | 'UnsubscribeVersion';
   }
 
-  /** @name XcmV1Order (211) */
+  /** @name XcmV1Order (212) */
   interface XcmV1Order extends Enum {
     readonly isNoop: boolean;
     readonly isDepositAsset: boolean;
@@ -2314,7 +2331,7 @@
     readonly type: 'Noop' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'QueryHolding' | 'BuyExecution';
   }
 
-  /** @name XcmV1Response (213) */
+  /** @name XcmV1Response (214) */
   interface XcmV1Response extends Enum {
     readonly isAssets: boolean;
     readonly asAssets: XcmV1MultiassetMultiAssets;
@@ -2323,10 +2340,10 @@
     readonly type: 'Assets' | 'Version';
   }
 
-  /** @name CumulusPalletXcmCall (227) */
+  /** @name CumulusPalletXcmCall (228) */
   type CumulusPalletXcmCall = Null;
 
-  /** @name CumulusPalletDmpQueueCall (228) */
+  /** @name CumulusPalletDmpQueueCall (229) */
   interface CumulusPalletDmpQueueCall extends Enum {
     readonly isServiceOverweight: boolean;
     readonly asServiceOverweight: {
@@ -2336,7 +2353,7 @@
     readonly type: 'ServiceOverweight';
   }
 
-  /** @name PalletInflationCall (229) */
+  /** @name PalletInflationCall (230) */
   interface PalletInflationCall extends Enum {
     readonly isStartInflation: boolean;
     readonly asStartInflation: {
@@ -2345,7 +2362,7 @@
     readonly type: 'StartInflation';
   }
 
-  /** @name PalletUniqueCall (230) */
+  /** @name PalletUniqueCall (231) */
   interface PalletUniqueCall extends Enum {
     readonly isCreateCollection: boolean;
     readonly asCreateCollection: {
@@ -2518,7 +2535,7 @@
     readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition' | 'SetAllowanceForAll' | 'ForceRepairCollection' | 'ForceRepairItem';
   }
 
-  /** @name UpDataStructsCollectionMode (235) */
+  /** @name UpDataStructsCollectionMode (236) */
   interface UpDataStructsCollectionMode extends Enum {
     readonly isNft: boolean;
     readonly isFungible: boolean;
@@ -2527,7 +2544,7 @@
     readonly type: 'Nft' | 'Fungible' | 'ReFungible';
   }
 
-  /** @name UpDataStructsCreateCollectionData (236) */
+  /** @name UpDataStructsCreateCollectionData (237) */
   interface UpDataStructsCreateCollectionData extends Struct {
     readonly mode: UpDataStructsCollectionMode;
     readonly access: Option<UpDataStructsAccessMode>;
@@ -2541,14 +2558,14 @@
     readonly properties: Vec<UpDataStructsProperty>;
   }
 
-  /** @name UpDataStructsAccessMode (238) */
+  /** @name UpDataStructsAccessMode (239) */
   interface UpDataStructsAccessMode extends Enum {
     readonly isNormal: boolean;
     readonly isAllowList: boolean;
     readonly type: 'Normal' | 'AllowList';
   }
 
-  /** @name UpDataStructsCollectionLimits (240) */
+  /** @name UpDataStructsCollectionLimits (241) */
   interface UpDataStructsCollectionLimits extends Struct {
     readonly accountTokenOwnershipLimit: Option<u32>;
     readonly sponsoredDataSize: Option<u32>;
@@ -2561,7 +2578,7 @@
     readonly transfersEnabled: Option<bool>;
   }
 
-  /** @name UpDataStructsSponsoringRateLimit (242) */
+  /** @name UpDataStructsSponsoringRateLimit (243) */
   interface UpDataStructsSponsoringRateLimit extends Enum {
     readonly isSponsoringDisabled: boolean;
     readonly isBlocks: boolean;
@@ -2569,43 +2586,43 @@
     readonly type: 'SponsoringDisabled' | 'Blocks';
   }
 
-  /** @name UpDataStructsCollectionPermissions (245) */
+  /** @name UpDataStructsCollectionPermissions (246) */
   interface UpDataStructsCollectionPermissions extends Struct {
     readonly access: Option<UpDataStructsAccessMode>;
     readonly mintMode: Option<bool>;
     readonly nesting: Option<UpDataStructsNestingPermissions>;
   }
 
-  /** @name UpDataStructsNestingPermissions (247) */
+  /** @name UpDataStructsNestingPermissions (248) */
   interface UpDataStructsNestingPermissions extends Struct {
     readonly tokenOwner: bool;
     readonly collectionAdmin: bool;
     readonly restricted: Option<UpDataStructsOwnerRestrictedSet>;
   }
 
-  /** @name UpDataStructsOwnerRestrictedSet (249) */
+  /** @name UpDataStructsOwnerRestrictedSet (250) */
   interface UpDataStructsOwnerRestrictedSet extends BTreeSet<u32> {}
 
-  /** @name UpDataStructsPropertyKeyPermission (254) */
+  /** @name UpDataStructsPropertyKeyPermission (255) */
   interface UpDataStructsPropertyKeyPermission extends Struct {
     readonly key: Bytes;
     readonly permission: UpDataStructsPropertyPermission;
   }
 
-  /** @name UpDataStructsPropertyPermission (255) */
+  /** @name UpDataStructsPropertyPermission (256) */
   interface UpDataStructsPropertyPermission extends Struct {
     readonly mutable: bool;
     readonly collectionAdmin: bool;
     readonly tokenOwner: bool;
   }
 
-  /** @name UpDataStructsProperty (258) */
+  /** @name UpDataStructsProperty (259) */
   interface UpDataStructsProperty extends Struct {
     readonly key: Bytes;
     readonly value: Bytes;
   }
 
-  /** @name UpDataStructsCreateItemData (261) */
+  /** @name UpDataStructsCreateItemData (262) */
   interface UpDataStructsCreateItemData extends Enum {
     readonly isNft: boolean;
     readonly asNft: UpDataStructsCreateNftData;
@@ -2616,23 +2633,23 @@
     readonly type: 'Nft' | 'Fungible' | 'ReFungible';
   }
 
-  /** @name UpDataStructsCreateNftData (262) */
+  /** @name UpDataStructsCreateNftData (263) */
   interface UpDataStructsCreateNftData extends Struct {
     readonly properties: Vec<UpDataStructsProperty>;
   }
 
-  /** @name UpDataStructsCreateFungibleData (263) */
+  /** @name UpDataStructsCreateFungibleData (264) */
   interface UpDataStructsCreateFungibleData extends Struct {
     readonly value: u128;
   }
 
-  /** @name UpDataStructsCreateReFungibleData (264) */
+  /** @name UpDataStructsCreateReFungibleData (265) */
   interface UpDataStructsCreateReFungibleData extends Struct {
     readonly pieces: u128;
     readonly properties: Vec<UpDataStructsProperty>;
   }
 
-  /** @name UpDataStructsCreateItemExData (267) */
+  /** @name UpDataStructsCreateItemExData (268) */
   interface UpDataStructsCreateItemExData extends Enum {
     readonly isNft: boolean;
     readonly asNft: Vec<UpDataStructsCreateNftExData>;
@@ -2645,26 +2662,26 @@
     readonly type: 'Nft' | 'Fungible' | 'RefungibleMultipleItems' | 'RefungibleMultipleOwners';
   }
 
-  /** @name UpDataStructsCreateNftExData (269) */
+  /** @name UpDataStructsCreateNftExData (270) */
   interface UpDataStructsCreateNftExData extends Struct {
     readonly properties: Vec<UpDataStructsProperty>;
     readonly owner: PalletEvmAccountBasicCrossAccountIdRepr;
   }
 
-  /** @name UpDataStructsCreateRefungibleExSingleOwner (276) */
+  /** @name UpDataStructsCreateRefungibleExSingleOwner (277) */
   interface UpDataStructsCreateRefungibleExSingleOwner extends Struct {
     readonly user: PalletEvmAccountBasicCrossAccountIdRepr;
     readonly pieces: u128;
     readonly properties: Vec<UpDataStructsProperty>;
   }
 
-  /** @name UpDataStructsCreateRefungibleExMultipleOwners (278) */
+  /** @name UpDataStructsCreateRefungibleExMultipleOwners (279) */
   interface UpDataStructsCreateRefungibleExMultipleOwners extends Struct {
     readonly users: BTreeMap<PalletEvmAccountBasicCrossAccountIdRepr, u128>;
     readonly properties: Vec<UpDataStructsProperty>;
   }
 
-  /** @name PalletConfigurationCall (279) */
+  /** @name PalletConfigurationCall (280) */
   interface PalletConfigurationCall extends Enum {
     readonly isSetWeightToFeeCoefficientOverride: boolean;
     readonly asSetWeightToFeeCoefficientOverride: {
@@ -2682,10 +2699,22 @@
     readonly asSetAppPromotionConfigurationOverride: {
       readonly configuration: PalletConfigurationAppPromotionConfiguration;
     } & Struct;
-    readonly type: 'SetWeightToFeeCoefficientOverride' | 'SetMinGasPriceOverride' | 'SetXcmAllowedLocations' | 'SetAppPromotionConfigurationOverride';
+    readonly isSetCollatorSelectionDesiredCollators: boolean;
+    readonly asSetCollatorSelectionDesiredCollators: {
+      readonly max: Option<u32>;
+    } & Struct;
+    readonly isSetCollatorSelectionLicenseBond: boolean;
+    readonly asSetCollatorSelectionLicenseBond: {
+      readonly amount: Option<u128>;
+    } & Struct;
+    readonly isSetCollatorSelectionKickThreshold: boolean;
+    readonly asSetCollatorSelectionKickThreshold: {
+      readonly threshold: Option<u32>;
+    } & Struct;
+    readonly type: 'SetWeightToFeeCoefficientOverride' | 'SetMinGasPriceOverride' | 'SetXcmAllowedLocations' | 'SetAppPromotionConfigurationOverride' | 'SetCollatorSelectionDesiredCollators' | 'SetCollatorSelectionLicenseBond' | 'SetCollatorSelectionKickThreshold';
   }
 
-  /** @name PalletConfigurationAppPromotionConfiguration (284) */
+  /** @name PalletConfigurationAppPromotionConfiguration (285) */
   interface PalletConfigurationAppPromotionConfiguration extends Struct {
     readonly recalculationInterval: Option<u32>;
     readonly pendingInterval: Option<u32>;
@@ -2693,13 +2722,13 @@
     readonly maxStakersPerCalculation: Option<u8>;
   }
 
-  /** @name PalletTemplateTransactionPaymentCall (288) */
+  /** @name PalletTemplateTransactionPaymentCall (289) */
   type PalletTemplateTransactionPaymentCall = Null;
 
-  /** @name PalletStructureCall (289) */
+  /** @name PalletStructureCall (290) */
   type PalletStructureCall = Null;
 
-  /** @name PalletRmrkCoreCall (290) */
+  /** @name PalletRmrkCoreCall (291) */
   interface PalletRmrkCoreCall extends Enum {
     readonly isCreateCollection: boolean;
     readonly asCreateCollection: {
@@ -2805,7 +2834,7 @@
     readonly type: 'CreateCollection' | 'DestroyCollection' | 'ChangeCollectionIssuer' | 'LockCollection' | 'MintNft' | 'BurnNft' | 'Send' | 'AcceptNft' | 'RejectNft' | 'AcceptResource' | 'AcceptResourceRemoval' | 'SetProperty' | 'SetPriority' | 'AddBasicResource' | 'AddComposableResource' | 'AddSlotResource' | 'RemoveResource';
   }
 
-  /** @name RmrkTraitsResourceResourceTypes (296) */
+  /** @name RmrkTraitsResourceResourceTypes (297) */
   interface RmrkTraitsResourceResourceTypes extends Enum {
     readonly isBasic: boolean;
     readonly asBasic: RmrkTraitsResourceBasicResource;
@@ -2816,7 +2845,7 @@
     readonly type: 'Basic' | 'Composable' | 'Slot';
   }
 
-  /** @name RmrkTraitsResourceBasicResource (298) */
+  /** @name RmrkTraitsResourceBasicResource (299) */
   interface RmrkTraitsResourceBasicResource extends Struct {
     readonly src: Option<Bytes>;
     readonly metadata: Option<Bytes>;
@@ -2824,7 +2853,7 @@
     readonly thumb: Option<Bytes>;
   }
 
-  /** @name RmrkTraitsResourceComposableResource (300) */
+  /** @name RmrkTraitsResourceComposableResource (301) */
   interface RmrkTraitsResourceComposableResource extends Struct {
     readonly parts: Vec<u32>;
     readonly base: u32;
@@ -2834,7 +2863,7 @@
     readonly thumb: Option<Bytes>;
   }
 
-  /** @name RmrkTraitsResourceSlotResource (301) */
+  /** @name RmrkTraitsResourceSlotResource (302) */
   interface RmrkTraitsResourceSlotResource extends Struct {
     readonly base: u32;
     readonly src: Option<Bytes>;
@@ -2844,7 +2873,7 @@
     readonly thumb: Option<Bytes>;
   }
 
-  /** @name PalletRmrkEquipCall (304) */
+  /** @name PalletRmrkEquipCall (305) */
   interface PalletRmrkEquipCall extends Enum {
     readonly isCreateBase: boolean;
     readonly asCreateBase: {
@@ -2866,7 +2895,7 @@
     readonly type: 'CreateBase' | 'ThemeAdd' | 'Equippable';
   }
 
-  /** @name RmrkTraitsPartPartType (307) */
+  /** @name RmrkTraitsPartPartType (308) */
   interface RmrkTraitsPartPartType extends Enum {
     readonly isFixedPart: boolean;
     readonly asFixedPart: RmrkTraitsPartFixedPart;
@@ -2875,14 +2904,14 @@
     readonly type: 'FixedPart' | 'SlotPart';
   }
 
-  /** @name RmrkTraitsPartFixedPart (309) */
+  /** @name RmrkTraitsPartFixedPart (310) */
   interface RmrkTraitsPartFixedPart extends Struct {
     readonly id: u32;
     readonly z: u32;
     readonly src: Bytes;
   }
 
-  /** @name RmrkTraitsPartSlotPart (310) */
+  /** @name RmrkTraitsPartSlotPart (311) */
   interface RmrkTraitsPartSlotPart extends Struct {
     readonly id: u32;
     readonly equippable: RmrkTraitsPartEquippableList;
@@ -2890,7 +2919,7 @@
     readonly z: u32;
   }
 
-  /** @name RmrkTraitsPartEquippableList (311) */
+  /** @name RmrkTraitsPartEquippableList (312) */
   interface RmrkTraitsPartEquippableList extends Enum {
     readonly isAll: boolean;
     readonly isEmpty: boolean;
@@ -2899,20 +2928,20 @@
     readonly type: 'All' | 'Empty' | 'Custom';
   }
 
-  /** @name RmrkTraitsTheme (313) */
+  /** @name RmrkTraitsTheme (314) */
   interface RmrkTraitsTheme extends Struct {
     readonly name: Bytes;
     readonly properties: Vec<RmrkTraitsThemeThemeProperty>;
     readonly inherit: bool;
   }
 
-  /** @name RmrkTraitsThemeThemeProperty (315) */
+  /** @name RmrkTraitsThemeThemeProperty (316) */
   interface RmrkTraitsThemeThemeProperty extends Struct {
     readonly key: Bytes;
     readonly value: Bytes;
   }
 
-  /** @name PalletAppPromotionCall (317) */
+  /** @name PalletAppPromotionCall (318) */
   interface PalletAppPromotionCall extends Enum {
     readonly isSetAdminAddress: boolean;
     readonly asSetAdminAddress: {
@@ -2946,7 +2975,7 @@
     readonly type: 'SetAdminAddress' | 'Stake' | 'Unstake' | 'SponsorCollection' | 'StopSponsoringCollection' | 'SponsorContract' | 'StopSponsoringContract' | 'PayoutStakers';
   }
 
-  /** @name PalletForeignAssetsModuleCall (318) */
+  /** @name PalletForeignAssetsModuleCall (319) */
   interface PalletForeignAssetsModuleCall extends Enum {
     readonly isRegisterForeignAsset: boolean;
     readonly asRegisterForeignAsset: {
@@ -2963,7 +2992,7 @@
     readonly type: 'RegisterForeignAsset' | 'UpdateForeignAsset';
   }
 
-  /** @name PalletEvmCall (319) */
+  /** @name PalletEvmCall (320) */
   interface PalletEvmCall extends Enum {
     readonly isWithdraw: boolean;
     readonly asWithdraw: {
@@ -3008,7 +3037,7 @@
     readonly type: 'Withdraw' | 'Call' | 'Create' | 'Create2';
   }
 
-  /** @name PalletEthereumCall (325) */
+  /** @name PalletEthereumCall (326) */
   interface PalletEthereumCall extends Enum {
     readonly isTransact: boolean;
     readonly asTransact: {
@@ -3017,7 +3046,7 @@
     readonly type: 'Transact';
   }
 
-  /** @name EthereumTransactionTransactionV2 (326) */
+  /** @name EthereumTransactionTransactionV2 (327) */
   interface EthereumTransactionTransactionV2 extends Enum {
     readonly isLegacy: boolean;
     readonly asLegacy: EthereumTransactionLegacyTransaction;
@@ -3028,7 +3057,7 @@
     readonly type: 'Legacy' | 'Eip2930' | 'Eip1559';
   }
 
-  /** @name EthereumTransactionLegacyTransaction (327) */
+  /** @name EthereumTransactionLegacyTransaction (328) */
   interface EthereumTransactionLegacyTransaction extends Struct {
     readonly nonce: U256;
     readonly gasPrice: U256;
@@ -3039,7 +3068,7 @@
     readonly signature: EthereumTransactionTransactionSignature;
   }
 
-  /** @name EthereumTransactionTransactionAction (328) */
+  /** @name EthereumTransactionTransactionAction (329) */
   interface EthereumTransactionTransactionAction extends Enum {
     readonly isCall: boolean;
     readonly asCall: H160;
@@ -3047,14 +3076,14 @@
     readonly type: 'Call' | 'Create';
   }
 
-  /** @name EthereumTransactionTransactionSignature (329) */
+  /** @name EthereumTransactionTransactionSignature (330) */
   interface EthereumTransactionTransactionSignature extends Struct {
     readonly v: u64;
     readonly r: H256;
     readonly s: H256;
   }
 
-  /** @name EthereumTransactionEip2930Transaction (331) */
+  /** @name EthereumTransactionEip2930Transaction (332) */
   interface EthereumTransactionEip2930Transaction extends Struct {
     readonly chainId: u64;
     readonly nonce: U256;
@@ -3069,13 +3098,13 @@
     readonly s: H256;
   }
 
-  /** @name EthereumTransactionAccessListItem (333) */
+  /** @name EthereumTransactionAccessListItem (334) */
   interface EthereumTransactionAccessListItem extends Struct {
     readonly address: H160;
     readonly storageKeys: Vec<H256>;
   }
 
-  /** @name EthereumTransactionEip1559Transaction (334) */
+  /** @name EthereumTransactionEip1559Transaction (335) */
   interface EthereumTransactionEip1559Transaction extends Struct {
     readonly chainId: u64;
     readonly nonce: U256;
@@ -3091,7 +3120,7 @@
     readonly s: H256;
   }
 
-  /** @name PalletEvmMigrationCall (335) */
+  /** @name PalletEvmMigrationCall (336) */
   interface PalletEvmMigrationCall extends Enum {
     readonly isBegin: boolean;
     readonly asBegin: {
@@ -3118,14 +3147,14 @@
     readonly type: 'Begin' | 'SetData' | 'Finish' | 'InsertEthLogs' | 'InsertEvents';
   }
 
-  /** @name PalletMaintenanceCall (339) */
+  /** @name PalletMaintenanceCall (340) */
   interface PalletMaintenanceCall extends Enum {
     readonly isEnable: boolean;
     readonly isDisable: boolean;
     readonly type: 'Enable' | 'Disable';
   }
 
-  /** @name PalletTestUtilsCall (340) */
+  /** @name PalletTestUtilsCall (341) */
   interface PalletTestUtilsCall extends Enum {
     readonly isEnable: boolean;
     readonly isSetTestValue: boolean;
@@ -3145,13 +3174,13 @@
     readonly type: 'Enable' | 'SetTestValue' | 'SetTestValueAndRollback' | 'IncTestValue' | 'JustTakeFee' | 'BatchAll';
   }
 
-  /** @name PalletSudoError (342) */
+  /** @name PalletSudoError (343) */
   interface PalletSudoError extends Enum {
     readonly isRequireSudo: boolean;
     readonly type: 'RequireSudo';
   }
 
-  /** @name OrmlVestingModuleError (344) */
+  /** @name OrmlVestingModuleError (345) */
   interface OrmlVestingModuleError extends Enum {
     readonly isZeroVestingPeriod: boolean;
     readonly isZeroVestingPeriodCount: boolean;
@@ -3162,7 +3191,7 @@
     readonly type: 'ZeroVestingPeriod' | 'ZeroVestingPeriodCount' | 'InsufficientBalanceToLock' | 'TooManyVestingSchedules' | 'AmountLow' | 'MaxVestingSchedulesExceeded';
   }
 
-  /** @name OrmlXtokensModuleError (345) */
+  /** @name OrmlXtokensModuleError (346) */
   interface OrmlXtokensModuleError extends Enum {
     readonly isAssetHasNoReserve: boolean;
     readonly isNotCrossChainTransfer: boolean;
@@ -3186,26 +3215,26 @@
     readonly type: 'AssetHasNoReserve' | 'NotCrossChainTransfer' | 'InvalidDest' | 'NotCrossChainTransferableCurrency' | 'UnweighableMessage' | 'XcmExecutionFailed' | 'CannotReanchor' | 'InvalidAncestry' | 'InvalidAsset' | 'DestinationNotInvertible' | 'BadVersion' | 'DistinctReserveForAssetAndFee' | 'ZeroFee' | 'ZeroAmount' | 'TooManyAssetsBeingSent' | 'AssetIndexNonExistent' | 'FeeNotEnough' | 'NotSupportedMultiLocation' | 'MinXcmFeeNotDefined';
   }
 
-  /** @name OrmlTokensBalanceLock (348) */
+  /** @name OrmlTokensBalanceLock (349) */
   interface OrmlTokensBalanceLock extends Struct {
     readonly id: U8aFixed;
     readonly amount: u128;
   }
 
-  /** @name OrmlTokensAccountData (350) */
+  /** @name OrmlTokensAccountData (351) */
   interface OrmlTokensAccountData extends Struct {
     readonly free: u128;
     readonly reserved: u128;
     readonly frozen: u128;
   }
 
-  /** @name OrmlTokensReserveData (352) */
+  /** @name OrmlTokensReserveData (353) */
   interface OrmlTokensReserveData extends Struct {
     readonly id: Null;
     readonly amount: u128;
   }
 
-  /** @name OrmlTokensModuleError (354) */
+  /** @name OrmlTokensModuleError (355) */
   interface OrmlTokensModuleError extends Enum {
     readonly isBalanceTooLow: boolean;
     readonly isAmountIntoBalanceFailed: boolean;
@@ -3218,21 +3247,21 @@
     readonly type: 'BalanceTooLow' | 'AmountIntoBalanceFailed' | 'LiquidityRestrictions' | 'MaxLocksExceeded' | 'KeepAlive' | 'ExistentialDeposit' | 'DeadAccount' | 'TooManyReserves';
   }
 
-  /** @name CumulusPalletXcmpQueueInboundChannelDetails (356) */
+  /** @name CumulusPalletXcmpQueueInboundChannelDetails (357) */
   interface CumulusPalletXcmpQueueInboundChannelDetails extends Struct {
     readonly sender: u32;
     readonly state: CumulusPalletXcmpQueueInboundState;
     readonly messageMetadata: Vec<ITuple<[u32, PolkadotParachainPrimitivesXcmpMessageFormat]>>;
   }
 
-  /** @name CumulusPalletXcmpQueueInboundState (357) */
+  /** @name CumulusPalletXcmpQueueInboundState (358) */
   interface CumulusPalletXcmpQueueInboundState extends Enum {
     readonly isOk: boolean;
     readonly isSuspended: boolean;
     readonly type: 'Ok' | 'Suspended';
   }
 
-  /** @name PolkadotParachainPrimitivesXcmpMessageFormat (360) */
+  /** @name PolkadotParachainPrimitivesXcmpMessageFormat (361) */
   interface PolkadotParachainPrimitivesXcmpMessageFormat extends Enum {
     readonly isConcatenatedVersionedXcm: boolean;
     readonly isConcatenatedEncodedBlob: boolean;
@@ -3240,7 +3269,7 @@
     readonly type: 'ConcatenatedVersionedXcm' | 'ConcatenatedEncodedBlob' | 'Signals';
   }
 
-  /** @name CumulusPalletXcmpQueueOutboundChannelDetails (363) */
+  /** @name CumulusPalletXcmpQueueOutboundChannelDetails (364) */
   interface CumulusPalletXcmpQueueOutboundChannelDetails extends Struct {
     readonly recipient: u32;
     readonly state: CumulusPalletXcmpQueueOutboundState;
@@ -3249,14 +3278,14 @@
     readonly lastIndex: u16;
   }
 
-  /** @name CumulusPalletXcmpQueueOutboundState (364) */
+  /** @name CumulusPalletXcmpQueueOutboundState (365) */
   interface CumulusPalletXcmpQueueOutboundState extends Enum {
     readonly isOk: boolean;
     readonly isSuspended: boolean;
     readonly type: 'Ok' | 'Suspended';
   }
 
-  /** @name CumulusPalletXcmpQueueQueueConfigData (366) */
+  /** @name CumulusPalletXcmpQueueQueueConfigData (367) */
   interface CumulusPalletXcmpQueueQueueConfigData extends Struct {
     readonly suspendThreshold: u32;
     readonly dropThreshold: u32;
@@ -3266,7 +3295,7 @@
     readonly xcmpMaxIndividualWeight: SpWeightsWeightV2Weight;
   }
 
-  /** @name CumulusPalletXcmpQueueError (368) */
+  /** @name CumulusPalletXcmpQueueError (369) */
   interface CumulusPalletXcmpQueueError extends Enum {
     readonly isFailedToSend: boolean;
     readonly isBadXcmOrigin: boolean;
@@ -3276,7 +3305,7 @@
     readonly type: 'FailedToSend' | 'BadXcmOrigin' | 'BadXcm' | 'BadOverweightIndex' | 'WeightOverLimit';
   }
 
-  /** @name PalletXcmError (369) */
+  /** @name PalletXcmError (370) */
   interface PalletXcmError extends Enum {
     readonly isUnreachable: boolean;
     readonly isSendFailure: boolean;
@@ -3294,29 +3323,29 @@
     readonly type: 'Unreachable' | 'SendFailure' | 'Filtered' | 'UnweighableMessage' | 'DestinationNotInvertible' | 'Empty' | 'CannotReanchor' | 'TooManyAssets' | 'InvalidOrigin' | 'BadVersion' | 'BadLocation' | 'NoSubscription' | 'AlreadySubscribed';
   }
 
-  /** @name CumulusPalletXcmError (370) */
+  /** @name CumulusPalletXcmError (371) */
   type CumulusPalletXcmError = Null;
 
-  /** @name CumulusPalletDmpQueueConfigData (371) */
+  /** @name CumulusPalletDmpQueueConfigData (372) */
   interface CumulusPalletDmpQueueConfigData extends Struct {
     readonly maxIndividual: SpWeightsWeightV2Weight;
   }
 
-  /** @name CumulusPalletDmpQueuePageIndexData (372) */
+  /** @name CumulusPalletDmpQueuePageIndexData (373) */
   interface CumulusPalletDmpQueuePageIndexData extends Struct {
     readonly beginUsed: u32;
     readonly endUsed: u32;
     readonly overweightCount: u64;
   }
 
-  /** @name CumulusPalletDmpQueueError (375) */
+  /** @name CumulusPalletDmpQueueError (376) */
   interface CumulusPalletDmpQueueError extends Enum {
     readonly isUnknown: boolean;
     readonly isOverLimit: boolean;
     readonly type: 'Unknown' | 'OverLimit';
   }
 
-  /** @name PalletUniqueError (379) */
+  /** @name PalletUniqueError (380) */
   interface PalletUniqueError extends Enum {
     readonly isCollectionDecimalPointLimitExceeded: boolean;
     readonly isEmptyArgument: boolean;
@@ -3324,13 +3353,13 @@
     readonly type: 'CollectionDecimalPointLimitExceeded' | 'EmptyArgument' | 'RepartitionCalledOnNonRefungibleCollection';
   }
 
-  /** @name PalletConfigurationError (380) */
+  /** @name PalletConfigurationError (381) */
   interface PalletConfigurationError extends Enum {
     readonly isInconsistentConfiguration: boolean;
     readonly type: 'InconsistentConfiguration';
   }
 
-  /** @name UpDataStructsCollection (381) */
+  /** @name UpDataStructsCollection (382) */
   interface UpDataStructsCollection extends Struct {
     readonly owner: AccountId32;
     readonly mode: UpDataStructsCollectionMode;
@@ -3343,7 +3372,7 @@
     readonly flags: U8aFixed;
   }
 
-  /** @name UpDataStructsSponsorshipStateAccountId32 (382) */
+  /** @name UpDataStructsSponsorshipStateAccountId32 (383) */
   interface UpDataStructsSponsorshipStateAccountId32 extends Enum {
     readonly isDisabled: boolean;
     readonly isUnconfirmed: boolean;
@@ -3353,43 +3382,43 @@
     readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed';
   }
 
-  /** @name UpDataStructsProperties (384) */
+  /** @name UpDataStructsProperties (385) */
   interface UpDataStructsProperties extends Struct {
     readonly map: UpDataStructsPropertiesMapBoundedVec;
     readonly consumedSpace: u32;
     readonly spaceLimit: u32;
   }
 
-  /** @name UpDataStructsPropertiesMapBoundedVec (385) */
+  /** @name UpDataStructsPropertiesMapBoundedVec (386) */
   interface UpDataStructsPropertiesMapBoundedVec extends BTreeMap<Bytes, Bytes> {}
 
-  /** @name UpDataStructsPropertiesMapPropertyPermission (390) */
+  /** @name UpDataStructsPropertiesMapPropertyPermission (391) */
   interface UpDataStructsPropertiesMapPropertyPermission extends BTreeMap<Bytes, UpDataStructsPropertyPermission> {}
 
-  /** @name UpDataStructsCollectionStats (397) */
+  /** @name UpDataStructsCollectionStats (398) */
   interface UpDataStructsCollectionStats extends Struct {
     readonly created: u32;
     readonly destroyed: u32;
     readonly alive: u32;
   }
 
-  /** @name UpDataStructsTokenChild (398) */
+  /** @name UpDataStructsTokenChild (399) */
   interface UpDataStructsTokenChild extends Struct {
     readonly token: u32;
     readonly collection: u32;
   }
 
-  /** @name PhantomTypeUpDataStructs (399) */
+  /** @name PhantomTypeUpDataStructs (400) */
   interface PhantomTypeUpDataStructs extends Vec<ITuple<[UpDataStructsTokenData, UpDataStructsRpcCollection, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftNftInfo, RmrkTraitsResourceResourceInfo, RmrkTraitsPropertyPropertyInfo, RmrkTraitsBaseBaseInfo, RmrkTraitsPartPartType, RmrkTraitsTheme, RmrkTraitsNftNftChild, UpPovEstimateRpcPovInfo]>> {}
 
-  /** @name UpDataStructsTokenData (401) */
+  /** @name UpDataStructsTokenData (402) */
   interface UpDataStructsTokenData extends Struct {
     readonly properties: Vec<UpDataStructsProperty>;
     readonly owner: Option<PalletEvmAccountBasicCrossAccountIdRepr>;
     readonly pieces: u128;
   }
 
-  /** @name UpDataStructsRpcCollection (403) */
+  /** @name UpDataStructsRpcCollection (404) */
   interface UpDataStructsRpcCollection extends Struct {
     readonly owner: AccountId32;
     readonly mode: UpDataStructsCollectionMode;
@@ -3405,13 +3434,13 @@
     readonly flags: UpDataStructsRpcCollectionFlags;
   }
 
-  /** @name UpDataStructsRpcCollectionFlags (404) */
+  /** @name UpDataStructsRpcCollectionFlags (405) */
   interface UpDataStructsRpcCollectionFlags extends Struct {
     readonly foreign: bool;
     readonly erc721metadata: bool;
   }
 
-  /** @name RmrkTraitsCollectionCollectionInfo (405) */
+  /** @name RmrkTraitsCollectionCollectionInfo (406) */
   interface RmrkTraitsCollectionCollectionInfo extends Struct {
     readonly issuer: AccountId32;
     readonly metadata: Bytes;
@@ -3420,7 +3449,7 @@
     readonly nftsCount: u32;
   }
 
-  /** @name RmrkTraitsNftNftInfo (406) */
+  /** @name RmrkTraitsNftNftInfo (407) */
   interface RmrkTraitsNftNftInfo extends Struct {
     readonly owner: RmrkTraitsNftAccountIdOrCollectionNftTuple;
     readonly royalty: Option<RmrkTraitsNftRoyaltyInfo>;
@@ -3429,13 +3458,13 @@
     readonly pending: bool;
   }
 
-  /** @name RmrkTraitsNftRoyaltyInfo (408) */
+  /** @name RmrkTraitsNftRoyaltyInfo (409) */
   interface RmrkTraitsNftRoyaltyInfo extends Struct {
     readonly recipient: AccountId32;
     readonly amount: Permill;
   }
 
-  /** @name RmrkTraitsResourceResourceInfo (409) */
+  /** @name RmrkTraitsResourceResourceInfo (410) */
   interface RmrkTraitsResourceResourceInfo extends Struct {
     readonly id: u32;
     readonly resource: RmrkTraitsResourceResourceTypes;
@@ -3443,26 +3472,26 @@
     readonly pendingRemoval: bool;
   }
 
-  /** @name RmrkTraitsPropertyPropertyInfo (410) */
+  /** @name RmrkTraitsPropertyPropertyInfo (411) */
   interface RmrkTraitsPropertyPropertyInfo extends Struct {
     readonly key: Bytes;
     readonly value: Bytes;
   }
 
-  /** @name RmrkTraitsBaseBaseInfo (411) */
+  /** @name RmrkTraitsBaseBaseInfo (412) */
   interface RmrkTraitsBaseBaseInfo extends Struct {
     readonly issuer: AccountId32;
     readonly baseType: Bytes;
     readonly symbol: Bytes;
   }
 
-  /** @name RmrkTraitsNftNftChild (412) */
+  /** @name RmrkTraitsNftNftChild (413) */
   interface RmrkTraitsNftNftChild extends Struct {
     readonly collectionId: u32;
     readonly nftId: u32;
   }
 
-  /** @name UpPovEstimateRpcPovInfo (413) */
+  /** @name UpPovEstimateRpcPovInfo (414) */
   interface UpPovEstimateRpcPovInfo extends Struct {
     readonly proofSize: u64;
     readonly compactProofSize: u64;
@@ -3471,7 +3500,7 @@
     readonly keyValues: Vec<UpPovEstimateRpcTrieKeyValue>;
   }
 
-  /** @name SpRuntimeTransactionValidityTransactionValidityError (416) */
+  /** @name SpRuntimeTransactionValidityTransactionValidityError (417) */
   interface SpRuntimeTransactionValidityTransactionValidityError extends Enum {
     readonly isInvalid: boolean;
     readonly asInvalid: SpRuntimeTransactionValidityInvalidTransaction;
@@ -3480,7 +3509,7 @@
     readonly type: 'Invalid' | 'Unknown';
   }
 
-  /** @name SpRuntimeTransactionValidityInvalidTransaction (417) */
+  /** @name SpRuntimeTransactionValidityInvalidTransaction (418) */
   interface SpRuntimeTransactionValidityInvalidTransaction extends Enum {
     readonly isCall: boolean;
     readonly isPayment: boolean;
@@ -3497,7 +3526,7 @@
     readonly type: 'Call' | 'Payment' | 'Future' | 'Stale' | 'BadProof' | 'AncientBirthBlock' | 'ExhaustsResources' | 'Custom' | 'BadMandatory' | 'MandatoryValidation' | 'BadSigner';
   }
 
-  /** @name SpRuntimeTransactionValidityUnknownTransaction (418) */
+  /** @name SpRuntimeTransactionValidityUnknownTransaction (419) */
   interface SpRuntimeTransactionValidityUnknownTransaction extends Enum {
     readonly isCannotLookup: boolean;
     readonly isNoUnsignedValidator: boolean;
@@ -3506,13 +3535,13 @@
     readonly type: 'CannotLookup' | 'NoUnsignedValidator' | 'Custom';
   }
 
-  /** @name UpPovEstimateRpcTrieKeyValue (420) */
+  /** @name UpPovEstimateRpcTrieKeyValue (421) */
   interface UpPovEstimateRpcTrieKeyValue extends Struct {
     readonly key: Bytes;
     readonly value: Bytes;
   }
 
-  /** @name PalletCommonError (422) */
+  /** @name PalletCommonError (423) */
   interface PalletCommonError extends Enum {
     readonly isCollectionNotFound: boolean;
     readonly isMustBeTokenOwner: boolean;
@@ -3553,7 +3582,7 @@
     readonly type: 'CollectionNotFound' | 'MustBeTokenOwner' | 'NoPermission' | 'CantDestroyNotEmptyCollection' | 'PublicMintingNotAllowed' | 'AddressNotInAllowlist' | 'CollectionNameLimitExceeded' | 'CollectionDescriptionLimitExceeded' | 'CollectionTokenPrefixLimitExceeded' | 'TotalCollectionsLimitExceeded' | 'CollectionAdminCountExceeded' | 'CollectionLimitBoundsExceeded' | 'OwnerPermissionsCantBeReverted' | 'TransferNotAllowed' | 'AccountTokenLimitExceeded' | 'CollectionTokenLimitExceeded' | 'MetadataFlagFrozen' | 'TokenNotFound' | 'TokenValueTooLow' | 'ApprovedValueTooLow' | 'CantApproveMoreThanOwned' | 'AddressIsZero' | 'UnsupportedOperation' | 'NotSufficientFounds' | 'UserIsNotAllowedToNest' | 'SourceCollectionIsNotAllowedToNest' | 'CollectionFieldSizeExceeded' | 'NoSpaceForProperty' | 'PropertyLimitReached' | 'PropertyKeyIsTooLong' | 'InvalidCharacterInPropertyKey' | 'EmptyPropertyKey' | 'CollectionIsExternal' | 'CollectionIsInternal' | 'ConfirmSponsorshipFail' | 'UserIsNotCollectionAdmin';
   }
 
-  /** @name PalletFungibleError (424) */
+  /** @name PalletFungibleError (425) */
   interface PalletFungibleError extends Enum {
     readonly isNotFungibleDataUsedToMintFungibleCollectionToken: boolean;
     readonly isFungibleItemsHaveNoId: boolean;
@@ -3565,7 +3594,7 @@
     readonly type: 'NotFungibleDataUsedToMintFungibleCollectionToken' | 'FungibleItemsHaveNoId' | 'FungibleItemsDontHaveData' | 'FungibleDisallowsNesting' | 'SettingPropertiesNotAllowed' | 'SettingAllowanceForAllNotAllowed' | 'FungibleTokensAreAlwaysValid';
   }
 
-  /** @name PalletRefungibleError (428) */
+  /** @name PalletRefungibleError (429) */
   interface PalletRefungibleError extends Enum {
     readonly isNotRefungibleDataUsedToMintFungibleCollectionToken: boolean;
     readonly isWrongRefungiblePieces: boolean;
@@ -3575,19 +3604,19 @@
     readonly type: 'NotRefungibleDataUsedToMintFungibleCollectionToken' | 'WrongRefungiblePieces' | 'RepartitionWhileNotOwningAllPieces' | 'RefungibleDisallowsNesting' | 'SettingPropertiesNotAllowed';
   }
 
-  /** @name PalletNonfungibleItemData (429) */
+  /** @name PalletNonfungibleItemData (430) */
   interface PalletNonfungibleItemData extends Struct {
     readonly owner: PalletEvmAccountBasicCrossAccountIdRepr;
   }
 
-  /** @name UpDataStructsPropertyScope (431) */
+  /** @name UpDataStructsPropertyScope (432) */
   interface UpDataStructsPropertyScope extends Enum {
     readonly isNone: boolean;
     readonly isRmrk: boolean;
     readonly type: 'None' | 'Rmrk';
   }
 
-  /** @name PalletNonfungibleError (434) */
+  /** @name PalletNonfungibleError (435) */
   interface PalletNonfungibleError extends Enum {
     readonly isNotNonfungibleDataUsedToMintFungibleCollectionToken: boolean;
     readonly isNonfungibleItemsHaveNoAmount: boolean;
@@ -3595,7 +3624,7 @@
     readonly type: 'NotNonfungibleDataUsedToMintFungibleCollectionToken' | 'NonfungibleItemsHaveNoAmount' | 'CantBurnNftWithChildren';
   }
 
-  /** @name PalletStructureError (435) */
+  /** @name PalletStructureError (436) */
   interface PalletStructureError extends Enum {
     readonly isOuroborosDetected: boolean;
     readonly isDepthLimit: boolean;
@@ -3604,7 +3633,7 @@
     readonly type: 'OuroborosDetected' | 'DepthLimit' | 'BreadthLimit' | 'TokenNotFound';
   }
 
-  /** @name PalletRmrkCoreError (436) */
+  /** @name PalletRmrkCoreError (437) */
   interface PalletRmrkCoreError extends Enum {
     readonly isCorruptedCollectionType: boolean;
     readonly isRmrkPropertyKeyIsTooLong: boolean;
@@ -3628,7 +3657,7 @@
     readonly type: 'CorruptedCollectionType' | 'RmrkPropertyKeyIsTooLong' | 'RmrkPropertyValueIsTooLong' | 'RmrkPropertyIsNotFound' | 'UnableToDecodeRmrkData' | 'CollectionNotEmpty' | 'NoAvailableCollectionId' | 'NoAvailableNftId' | 'CollectionUnknown' | 'NoPermission' | 'NonTransferable' | 'CollectionFullOrLocked' | 'ResourceDoesntExist' | 'CannotSendToDescendentOrSelf' | 'CannotAcceptNonOwnedNft' | 'CannotRejectNonOwnedNft' | 'CannotRejectNonPendingNft' | 'ResourceNotPending' | 'NoAvailableResourceId';
   }
 
-  /** @name PalletRmrkEquipError (438) */
+  /** @name PalletRmrkEquipError (439) */
   interface PalletRmrkEquipError extends Enum {
     readonly isPermissionError: boolean;
     readonly isNoAvailableBaseId: boolean;
@@ -3640,7 +3669,7 @@
     readonly type: 'PermissionError' | 'NoAvailableBaseId' | 'NoAvailablePartId' | 'BaseDoesntExist' | 'NeedsDefaultThemeFirst' | 'PartDoesntExist' | 'NoEquippableOnFixedPart';
   }
 
-  /** @name PalletAppPromotionError (444) */
+  /** @name PalletAppPromotionError (445) */
   interface PalletAppPromotionError extends Enum {
     readonly isAdminNotSet: boolean;
     readonly isNoPermission: boolean;
@@ -3651,7 +3680,7 @@
     readonly type: 'AdminNotSet' | 'NoPermission' | 'NotSufficientFunds' | 'PendingForBlockOverflow' | 'SponsorNotSet' | 'IncorrectLockedBalanceOperation';
   }
 
-  /** @name PalletForeignAssetsModuleError (445) */
+  /** @name PalletForeignAssetsModuleError (446) */
   interface PalletForeignAssetsModuleError extends Enum {
     readonly isBadLocation: boolean;
     readonly isMultiLocationExisted: boolean;
@@ -3660,7 +3689,7 @@
     readonly type: 'BadLocation' | 'MultiLocationExisted' | 'AssetIdNotExists' | 'AssetIdExisted';
   }
 
-  /** @name PalletEvmError (447) */
+  /** @name PalletEvmError (448) */
   interface PalletEvmError extends Enum {
     readonly isBalanceLow: boolean;
     readonly isFeeOverflow: boolean;
@@ -3676,7 +3705,7 @@
     readonly type: 'BalanceLow' | 'FeeOverflow' | 'PaymentOverflow' | 'WithdrawFailed' | 'GasPriceTooLow' | 'InvalidNonce' | 'GasLimitTooLow' | 'GasLimitTooHigh' | 'Undefined' | 'Reentrancy' | 'TransactionMustComeFromEOA';
   }
 
-  /** @name FpRpcTransactionStatus (450) */
+  /** @name FpRpcTransactionStatus (451) */
   interface FpRpcTransactionStatus extends Struct {
     readonly transactionHash: H256;
     readonly transactionIndex: u32;
@@ -3687,10 +3716,10 @@
     readonly logsBloom: EthbloomBloom;
   }
 
-  /** @name EthbloomBloom (452) */
+  /** @name EthbloomBloom (453) */
   interface EthbloomBloom extends U8aFixed {}
 
-  /** @name EthereumReceiptReceiptV3 (454) */
+  /** @name EthereumReceiptReceiptV3 (455) */
   interface EthereumReceiptReceiptV3 extends Enum {
     readonly isLegacy: boolean;
     readonly asLegacy: EthereumReceiptEip658ReceiptData;
@@ -3701,7 +3730,7 @@
     readonly type: 'Legacy' | 'Eip2930' | 'Eip1559';
   }
 
-  /** @name EthereumReceiptEip658ReceiptData (455) */
+  /** @name EthereumReceiptEip658ReceiptData (456) */
   interface EthereumReceiptEip658ReceiptData extends Struct {
     readonly statusCode: u8;
     readonly usedGas: U256;
@@ -3709,14 +3738,14 @@
     readonly logs: Vec<EthereumLog>;
   }
 
-  /** @name EthereumBlock (456) */
+  /** @name EthereumBlock (457) */
   interface EthereumBlock extends Struct {
     readonly header: EthereumHeader;
     readonly transactions: Vec<EthereumTransactionTransactionV2>;
     readonly ommers: Vec<EthereumHeader>;
   }
 
-  /** @name EthereumHeader (457) */
+  /** @name EthereumHeader (458) */
   interface EthereumHeader extends Struct {
     readonly parentHash: H256;
     readonly ommersHash: H256;
@@ -3735,24 +3764,24 @@
     readonly nonce: EthereumTypesHashH64;
   }
 
-  /** @name EthereumTypesHashH64 (458) */
+  /** @name EthereumTypesHashH64 (459) */
   interface EthereumTypesHashH64 extends U8aFixed {}
 
-  /** @name PalletEthereumError (463) */
+  /** @name PalletEthereumError (464) */
   interface PalletEthereumError extends Enum {
     readonly isInvalidSignature: boolean;
     readonly isPreLogExists: boolean;
     readonly type: 'InvalidSignature' | 'PreLogExists';
   }
 
-  /** @name PalletEvmCoderSubstrateError (464) */
+  /** @name PalletEvmCoderSubstrateError (465) */
   interface PalletEvmCoderSubstrateError extends Enum {
     readonly isOutOfGas: boolean;
     readonly isOutOfFund: boolean;
     readonly type: 'OutOfGas' | 'OutOfFund';
   }
 
-  /** @name UpDataStructsSponsorshipStateBasicCrossAccountIdRepr (465) */
+  /** @name UpDataStructsSponsorshipStateBasicCrossAccountIdRepr (466) */
   interface UpDataStructsSponsorshipStateBasicCrossAccountIdRepr extends Enum {
     readonly isDisabled: boolean;
     readonly isUnconfirmed: boolean;
@@ -3762,7 +3791,7 @@
     readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed';
   }
 
-  /** @name PalletEvmContractHelpersSponsoringModeT (466) */
+  /** @name PalletEvmContractHelpersSponsoringModeT (467) */
   interface PalletEvmContractHelpersSponsoringModeT extends Enum {
     readonly isDisabled: boolean;
     readonly isAllowlisted: boolean;
@@ -3770,7 +3799,7 @@
     readonly type: 'Disabled' | 'Allowlisted' | 'Generous';
   }
 
-  /** @name PalletEvmContractHelpersError (472) */
+  /** @name PalletEvmContractHelpersError (473) */
   interface PalletEvmContractHelpersError extends Enum {
     readonly isNoPermission: boolean;
     readonly isNoPendingSponsor: boolean;
@@ -3778,7 +3807,7 @@
     readonly type: 'NoPermission' | 'NoPendingSponsor' | 'TooManyMethodsHaveSponsoredLimit';
   }
 
-  /** @name PalletEvmMigrationError (473) */
+  /** @name PalletEvmMigrationError (474) */
   interface PalletEvmMigrationError extends Enum {
     readonly isAccountNotEmpty: boolean;
     readonly isAccountIsNotMigrating: boolean;
@@ -3786,17 +3815,17 @@
     readonly type: 'AccountNotEmpty' | 'AccountIsNotMigrating' | 'BadEvent';
   }
 
-  /** @name PalletMaintenanceError (474) */
+  /** @name PalletMaintenanceError (475) */
   type PalletMaintenanceError = Null;
 
-  /** @name PalletTestUtilsError (475) */
+  /** @name PalletTestUtilsError (476) */
   interface PalletTestUtilsError extends Enum {
     readonly isTestPalletDisabled: boolean;
     readonly isTriggerRollback: boolean;
     readonly type: 'TestPalletDisabled' | 'TriggerRollback';
   }
 
-  /** @name SpRuntimeMultiSignature (477) */
+  /** @name SpRuntimeMultiSignature (478) */
   interface SpRuntimeMultiSignature extends Enum {
     readonly isEd25519: boolean;
     readonly asEd25519: SpCoreEd25519Signature;
@@ -3807,40 +3836,43 @@
     readonly type: 'Ed25519' | 'Sr25519' | 'Ecdsa';
   }
 
-  /** @name SpCoreEd25519Signature (478) */
+  /** @name SpCoreEd25519Signature (479) */
   interface SpCoreEd25519Signature extends U8aFixed {}
 
-  /** @name SpCoreSr25519Signature (480) */
+  /** @name SpCoreSr25519Signature (481) */
   interface SpCoreSr25519Signature extends U8aFixed {}
 
-  /** @name SpCoreEcdsaSignature (481) */
+  /** @name SpCoreEcdsaSignature (482) */
   interface SpCoreEcdsaSignature extends U8aFixed {}
 
-  /** @name FrameSystemExtensionsCheckSpecVersion (484) */
+  /** @name FrameSystemExtensionsCheckSpecVersion (485) */
   type FrameSystemExtensionsCheckSpecVersion = Null;
 
-  /** @name FrameSystemExtensionsCheckTxVersion (485) */
+  /** @name FrameSystemExtensionsCheckTxVersion (486) */
   type FrameSystemExtensionsCheckTxVersion = Null;
 
-  /** @name FrameSystemExtensionsCheckGenesis (486) */
+  /** @name FrameSystemExtensionsCheckGenesis (487) */
   type FrameSystemExtensionsCheckGenesis = Null;
 
-  /** @name FrameSystemExtensionsCheckNonce (489) */
+  /** @name FrameSystemExtensionsCheckNonce (490) */
   interface FrameSystemExtensionsCheckNonce extends Compact<u32> {}
 
-  /** @name FrameSystemExtensionsCheckWeight (490) */
+  /** @name FrameSystemExtensionsCheckWeight (491) */
   type FrameSystemExtensionsCheckWeight = Null;
 
-  /** @name OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance (491) */
+  /** @name OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance (492) */
   type OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance = Null;
 
-  /** @name PalletTemplateTransactionPaymentChargeTransactionPayment (492) */
+  /** @name OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls (493) */
+  type OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls = Null;
+
+  /** @name PalletTemplateTransactionPaymentChargeTransactionPayment (494) */
   interface PalletTemplateTransactionPaymentChargeTransactionPayment extends Compact<u128> {}
 
-  /** @name OpalRuntimeRuntime (493) */
+  /** @name OpalRuntimeRuntime (495) */
   type OpalRuntimeRuntime = Null;
 
-  /** @name PalletEthereumFakeTransactionFinalizer (494) */
+  /** @name PalletEthereumFakeTransactionFinalizer (496) */
   type PalletEthereumFakeTransactionFinalizer = Null;
 
 } // declare module
modifiedtests/src/pallet-presence.test.tsdiffbeforeafterboth
--- a/tests/src/pallet-presence.test.ts
+++ b/tests/src/pallet-presence.test.ts
@@ -66,6 +66,7 @@
       const foreignAssets = 'foreignassets';
       const rmrkPallets = ['rmrkcore', 'rmrkequip'];
       const appPromotion = 'apppromotion';
+      const collatorSelection = ['authorship', 'session', 'collatorselection', 'identity'];
       const testUtils = 'testutils';
 
       if (chain.eq('OPAL by UNIQUE')) {
@@ -81,6 +82,7 @@
           refungible,
           appPromotion,
           foreignAssets,
+          ...collatorSelection,
         );
       } else if (chain.eq('UNIQUE')) {
         // Insert Unique additional pallets here
addedtests/src/util/identitySetter.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/util/identitySetter.ts
@@ -0,0 +1,86 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+import {encodeAddress} from '@polkadot/keyring';
+import {usingPlaygrounds, Pallets} from './index';
+import {ChainHelperBase} from './playgrounds/unique';
+
+const relayUrl = process.argv[2] ?? 'ws://localhost:9844';
+const paraUrl = process.argv[3] ?? 'ws://localhost:9944';
+const key = process.argv.length > 4 ? process.argv.slice(4).join(' ') : '//Alice';
+
+function extractIdentity(key: any, value: any): [string, any] {
+  return [(key as any).toHuman()[0], (value as any).unwrap()];
+}
+
+async function getIdentities(helper: ChainHelperBase) {
+  const identities: [string, any][] = [];
+  for(const [key, value] of await helper.getApi().query.identity.identityOf.entries())
+    identities.push(extractIdentity(key, value));
+  return identities;
+}
+
+// This is a utility for pulling
+const forceInsertIdentities = async (): Promise<void> => {
+  const identitiesOnRelay: any[] = [];
+  const identitiesToRemove: string[] = [];
+  await usingPlaygrounds(async helper => {
+    try {
+      // iterate over every identity
+      for(const [key, v] of await helper.getApi().query.identity.identityOf.entries()) {
+        const value = v as any;
+        if (value.isNone) {
+          // in the nigh-impossible case that storage map would actually give None for a value, might as well delete it
+          identitiesToRemove.push((key as any).toHuman()[0]);
+          continue;
+        }
+
+        // if any of the judgements resulted in a good confirmed outcome, keep this identity
+        if (value.unwrap().toHuman().judgements.filter((x: any) => x[1] == 'Reasonable' || x[1] == 'KnownGood').length == 0) continue;
+        identitiesOnRelay.push(extractIdentity(key, value));
+      }
+    } catch (error) {
+      console.error(error);
+      throw Error('Error during fetching identities');
+    }
+  }, relayUrl);
+
+  await usingPlaygrounds(async (helper, privateKey) => {
+    if (helper.fetchMissingPalletNames([Pallets.Identity]).length != 0) console.error('pallet-identity is not included in parachain.');
+    try {
+      const superuser = await privateKey(key);
+      const ss58Format = helper.chain.getChainProperties().ss58Format;
+      const paraIdentities = await getIdentities(helper);
+      const identitiesToAdd: any[] = [];
+
+      // cross-reference every account for changes
+      for (const [key, value] of identitiesOnRelay) {
+        const encodedKey = encodeAddress(key, ss58Format);
+
+        const identity = paraIdentities.find(i => i[0] === encodedKey);
+        if (identity) {
+          // only update if the identity info does not exist or is changed
+          if (value.toString() === identity[1].toString()) {
+            continue;
+          }
+        }
+        identitiesToAdd.push([key, value]);
+        // exercise caution - in case we have an identity and the realy doesn't, it might mean one of two things:
+        // 1) it was deleted on the relay;
+        // 2) it is our own identity, we don't want to delete it.
+        // identitiesToRemove.push((key as any).toHuman()[0]);
+      }
+
+      // await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceRemoveIdentities', [identitiesToRemove]);
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [identitiesToAdd]);
+      console.log(`Tried to upload ${identitiesToAdd.length} identities `
+        + `and found ${identitiesToRemove.length} identities for potential removal. `
+        + `Now there are ${(await helper.getApi().query.identity.identityOf.keys()).length}.`);
+    } catch (error) {
+      console.error(error);
+      throw Error('Error during setting identities');
+    }
+  }, paraUrl);
+};
+
+forceInsertIdentities().catch(() => process.exit(1));
\ No newline at end of file
modifiedtests/src/util/index.tsdiffbeforeafterboth
--- a/tests/src/util/index.ts
+++ b/tests/src/util/index.ts
@@ -110,6 +110,9 @@
   NFT = 'nonfungible',
   Scheduler = 'scheduler',
   AppPromotion = 'apppromotion',
+  CollatorSelection = 'collatorselection',
+  Session = 'session',
+  Identity = 'identity',
   TestUtils = 'testutils',
 }
 
modifiedtests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/unique.dev.ts
+++ b/tests/src/util/playgrounds/unique.dev.ts
@@ -66,6 +66,7 @@
   arrange: ArrangeGroup;
   wait: WaitGroup;
   admin: AdminGroup;
+  session: SessionGroup;
   testUtils: TestUtilGroup;
 
   constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
@@ -76,6 +77,7 @@
     this.wait = new WaitGroup(this);
     this.admin = new AdminGroup(this);
     this.testUtils = new TestUtilGroup(this);
+    this.session = new SessionGroup(this);
   }
 
   async connect(wsEndpoint: string, _listeners?: any): Promise<void> {
@@ -91,6 +93,10 @@
           extrinsic: {},
           payload: {},
         },
+        DisableIdentityCalls: {
+          extrinsic: {},
+          payload: {},
+        },
         FakeTransactionFinalizer: {
           extrinsic: {},
           payload: {},
@@ -251,16 +257,17 @@
       const accounts: IKeyringPair[] = [];
       let nonce = await this.helper.chain.getNonce(donor.address);
       const tokenNominal = this.helper.balance.getOneTokenNominal();
+      const ss58Format = this.helper.chain.getChainProperties().ss58Format;
       for (let i = 0; i < accountsToCreate; i++) {
         if (i === 500) { // if there are too many accounts to create
           await Promise.allSettled(transactions); // wait while first 500 (should be 100 for devnode) tx will be settled
           transactions = []; //
           nonce = await this.helper.chain.getNonce(donor.address); // update nonce
         }
-        const recepient = this.helper.util.fromSeed(mnemonicGenerate());
-        accounts.push(recepient);
+        const recipient = this.helper.util.fromSeed(mnemonicGenerate(), ss58Format);
+        accounts.push(recipient);
         if (withBalance !== 0n) {
-          const tx = this.helper.constructApiCall('api.tx.balances.transfer', [{Id: recepient.address}, withBalance * tokenNominal]);
+          const tx = this.helper.constructApiCall('api.tx.balances.transfer', [{Id: recipient.address}, withBalance * tokenNominal]);
           transactions.push(this.helper.signTransaction(donor, tx, {nonce}, 'account generation'));
           nonce++;
         }
@@ -337,7 +344,7 @@
     const kvStr = JSON.stringify(kvJson);
 
     const chainql = spawnSync(
-      'chainql', 
+      'chainql',
       [
         `--tla-code=data=${kvStr}`,
         '-e', `function(data) cql.dump(cql.chain("${this.helper.getEndpoint()}").latest._meta, data, {omit_empty:true})`,
@@ -457,6 +464,27 @@
   }
 
   /**
+   * Launch some async operation, or throw an error after some time. Note that it will still continue executing after the timeout.
+   * @param promise async operation to race against the timeout
+   * @param timeoutMS time after which to time out
+   * @param timeoutError error message to throw
+   * @returns promise of the same type the operation had
+   */
+  withTimeout<T>(
+    promise: Promise<T>,
+    timeoutMS = 30000,
+    timeoutError = 'The operation has timed out!',
+  ): Promise<T> {
+    const timeout = new Promise<never>((_, reject) => {
+      setTimeout(() => {
+        reject(new Error(timeoutError));
+      }, timeoutMS);
+    });
+
+    return Promise.race<T>([promise, timeout]).catch(e => {throw new Error(e);});
+  }
+
+  /**
    * Wait for specified number of blocks
    * @param blocksCount number of blocks to wait
    * @returns
@@ -478,6 +506,30 @@
     return promise;
   }
 
+  /**
+   * Wait for the specified number of sessions to pass.
+   * Only applicable if the Session pallet is turned on.
+   * @param sessionCount number of sessions to wait
+   * @param blockTimeout time in ms until panicking that the chain has stopped producing blocks
+   * @returns
+   */
+  async newSessions(sessionCount = 1, blockTimeout = 60000): Promise<void> {
+    console.log(`Waiting for ${sessionCount} new session${sessionCount > 1 ? 's' : ''}.`
+      + ' This might take a while -- check SessionPeriod in pallet_session::Config for session time.');
+
+    const expectedSessionIndex = await (this.helper as DevUniqueHelper).session.getIndex() + sessionCount;
+    let currentSessionIndex = -1;
+
+    while (currentSessionIndex < expectedSessionIndex) {
+      // eslint-disable-next-line no-async-promise-executor
+      currentSessionIndex = await this.withTimeout(new Promise(async (resolve) => {
+        await this.newBlocks(1);
+        const res = await (this.helper as DevUniqueHelper).session.getIndex();
+        resolve(res);
+      }), blockTimeout, 'The chain has stopped producing blocks!');
+    }
+  }
+
   async forParachainBlockNumber(blockNumber: bigint | number, timeout?: number) {
     timeout = timeout ?? 30 * 60 * 1000;
     // eslint-disable-next-line no-async-promise-executor
@@ -561,6 +613,36 @@
   }
 }
 
+class SessionGroup {
+  helper: ChainHelperBase;
+
+  constructor(helper: ChainHelperBase) {
+    this.helper = helper;
+  }
+
+  //todo:collator documentation
+  async getIndex(): Promise<number> {
+    return (await this.helper.callRpc('api.query.session.currentIndex')).toNumber();
+  }
+
+  newSessions(sessionCount = 1, blockTimeout = 24000): Promise<void> {
+    return (this.helper as DevUniqueHelper).wait.newSessions(sessionCount, blockTimeout);
+  }
+
+  setOwnKeys(signer: TSigner, key: string) {
+    return this.helper.executeExtrinsic(
+      signer,
+      'api.tx.session.setKeys',
+      [key, '0x0'],
+      true,
+    );
+  }
+
+  setOwnKeysFromAddress(signer: TSigner) {
+    return this.setOwnKeys(signer, '0x' + Buffer.from(signer.addressRaw).toString('hex'));
+  }
+}
+
 class TestUtilGroup {
   helper: DevUniqueHelper;
 
modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
after · tests/src/util/playgrounds/unique.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// SPDX-License-Identifier: Apache-2.034/* eslint-disable @typescript-eslint/no-var-requires */5/* eslint-disable function-call-argument-newline */6/* eslint-disable no-prototype-builtins */78import {ApiPromise, WsProvider, Keyring} from '@polkadot/api';9import {ApiInterfaceEvents, SignerOptions} from '@polkadot/api/types';10import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm, base58Encode, blake2AsU8a} from '@polkadot/util-crypto';11import {IKeyringPair} from '@polkadot/types/types';12import {hexToU8a} from '@polkadot/util/hex';13import {u8aConcat} from '@polkadot/util/u8a';14import {15  IApiListeners,16  IBlock,17  IEvent,18  IChainProperties,19  ICollectionCreationOptions,20  ICollectionLimits,21  ICollectionPermissions,22  ICrossAccountId,23  ICrossAccountIdLower,24  ILogger,25  INestingPermissions,26  IProperty,27  IStakingInfo,28  ISchedulerOptions,29  ISubstrateBalance,30  IToken,31  ITokenPropertyPermission,32  ITransactionResult,33  IUniqueHelperLog,34  TApiAllowedListeners,35  TEthereumAccount,36  TSigner,37  TSubstrateAccount,38  TNetworks,39  IForeignAssetMetadata,40  AcalaAssetMetadata,41  MoonbeamAssetInfo,42  DemocracyStandardAccountVote,43  IEthCrossAccountId,44} from './types';45import {RuntimeDispatchInfo} from '@polkadot/types/interfaces';46import type {Vec} from '@polkadot/types-codec';47import {FrameSystemEventRecord} from '@polkadot/types/lookup';4849export class CrossAccountId implements ICrossAccountId {50  Substrate?: TSubstrateAccount;51  Ethereum?: TEthereumAccount;5253  constructor(account: ICrossAccountId) {54    if (account.Substrate) this.Substrate = account.Substrate;55    if (account.Ethereum) this.Ethereum = account.Ethereum;56  }5758  static fromKeyring(account: IKeyringPair, domain: 'Substrate' | 'Ethereum' = 'Substrate') {59    switch (domain) {60      case 'Substrate': return new CrossAccountId({Substrate: account.address});61      case 'Ethereum': return new CrossAccountId({Substrate: account.address}).toEthereum();62    }63  }6465  static fromLowerCaseKeys(address: ICrossAccountIdLower): CrossAccountId {66    return new CrossAccountId({Substrate: address.substrate, Ethereum: address.ethereum});67  }6869  static normalizeSubstrateAddress(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {70    return encodeAddress(decodeAddress(address), ss58Format);71  }7273  static withNormalizedSubstrate(address: TSubstrateAccount, ss58Format = 42): CrossAccountId {74    return new CrossAccountId({Substrate: CrossAccountId.normalizeSubstrateAddress(address, ss58Format)});75  }7677  withNormalizedSubstrate(ss58Format = 42): CrossAccountId {78    if (this.Substrate) return CrossAccountId.withNormalizedSubstrate(this.Substrate, ss58Format);79    return this;80  }8182  static translateSubToEth(address: TSubstrateAccount): TEthereumAccount {83    return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(address), i => i.toString(16).padStart(2, '0')).join(''));84  }8586  toEthereum(): CrossAccountId {87    if (this.Substrate) return new CrossAccountId({Ethereum: CrossAccountId.translateSubToEth(this.Substrate)});88    return this;89  }9091  static translateEthToSub(address: TEthereumAccount, ss58Format?: number): TSubstrateAccount {92    return evmToAddress(address, ss58Format);93  }9495  toSubstrate(ss58Format?: number): CrossAccountId {96    if (this.Ethereum) return new CrossAccountId({Substrate: CrossAccountId.translateEthToSub(this.Ethereum, ss58Format)});97    return this;98  }99100  toLowerCase(): CrossAccountId {101    if (this.Substrate) this.Substrate = this.Substrate.toLowerCase();102    if (this.Ethereum) this.Ethereum = this.Ethereum.toLowerCase();103    return this;104  }105}106107const nesting = {108  toChecksumAddress(address: string): string {109    if (typeof address === 'undefined') return '';110111    if(!/^(0x)?[0-9a-f]{40}$/i.test(address)) throw new Error(`Given address "${address}" is not a valid Ethereum address.`);112113    address = address.toLowerCase().replace(/^0x/i,'');114    const addressHash = keccakAsHex(address).replace(/^0x/i,'');115    const checksumAddress = ['0x'];116117    for (let i = 0; i < address.length; i++) {118      // If ith character is 8 to f then make it uppercase119      if (parseInt(addressHash[i], 16) > 7) {120        checksumAddress.push(address[i].toUpperCase());121      } else {122        checksumAddress.push(address[i]);123      }124    }125    return checksumAddress.join('');126  },127  tokenIdToAddress(collectionId: number, tokenId: number) {128    return this.toChecksumAddress(`0xf8238ccfff8ed887463fd5e0${collectionId.toString(16).padStart(8, '0')}${tokenId.toString(16).padStart(8, '0')}`);129  },130};131132class UniqueUtil {133  static transactionStatus = {134    NOT_READY: 'NotReady',135    FAIL: 'Fail',136    SUCCESS: 'Success',137  };138139  static chainLogType = {140    EXTRINSIC: 'extrinsic',141    RPC: 'rpc',142  };143144  static getTokenAccount(token: IToken): CrossAccountId {145    return new CrossAccountId({Ethereum: this.getTokenAddress(token)});146  }147148  static getTokenAddress(token: IToken): string {149    return nesting.tokenIdToAddress(token.collectionId, token.tokenId);150  }151152  static getDefaultLogger(): ILogger {153    return {154      log(msg: any, level = 'INFO') {155        console[level.toLocaleLowerCase() === 'error' ? 'error' : 'log'](...(Array.isArray(msg) ? msg : [msg]));156      },157      level: {158        ERROR: 'ERROR',159        WARNING: 'WARNING',160        INFO: 'INFO',161      },162    };163  }164165  static vec2str(arr: string[] | number[]) {166    return arr.map(x => String.fromCharCode(parseInt(x.toString()))).join('');167  }168169  static str2vec(string: string) {170    if (typeof string !== 'string') return string;171    return Array.from(string).map(x => x.charCodeAt(0));172  }173174  static fromSeed(seed: string, ss58Format = 42) {175    const keyring = new Keyring({type: 'sr25519', ss58Format});176    return keyring.addFromUri(seed);177  }178179  static extractCollectionIdFromCreationResult(creationResult: ITransactionResult): number {180    if (creationResult.status !== this.transactionStatus.SUCCESS) {181      throw Error('Unable to create collection!');182    }183184    let collectionId = null;185    creationResult.result.events.forEach(({event: {data, method, section}}) => {186      if ((section === 'common') && (method === 'CollectionCreated')) {187        collectionId = parseInt(data[0].toString(), 10);188      }189    });190191    if (collectionId === null) {192      throw Error('No CollectionCreated event was found!');193    }194195    return collectionId;196  }197198  static extractTokensFromCreationResult(creationResult: ITransactionResult): {199    success: boolean,200    tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],201  } {202    if (creationResult.status !== this.transactionStatus.SUCCESS) {203      throw Error('Unable to create tokens!');204    }205    let success = false;206    const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];207    creationResult.result.events.forEach(({event: {data, method, section}}) => {208      if (method === 'ExtrinsicSuccess') {209        success = true;210      } else if ((section === 'common') && (method === 'ItemCreated')) {211        tokens.push({212          collectionId: parseInt(data[0].toString(), 10),213          tokenId: parseInt(data[1].toString(), 10),214          owner: data[2].toHuman(),215          amount: data[3].toBigInt(),216        });217      }218    });219    return {success, tokens};220  }221222  static extractTokensFromBurnResult(burnResult: ITransactionResult): {223    success: boolean,224    tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],225  } {226    if (burnResult.status !== this.transactionStatus.SUCCESS) {227      throw Error('Unable to burn tokens!');228    }229    let success = false;230    const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];231    burnResult.result.events.forEach(({event: {data, method, section}}) => {232      if (method === 'ExtrinsicSuccess') {233        success = true;234      } else if ((section === 'common') && (method === 'ItemDestroyed')) {235        tokens.push({236          collectionId: parseInt(data[0].toString(), 10),237          tokenId: parseInt(data[1].toString(), 10),238          owner: data[2].toHuman(),239          amount: data[3].toBigInt(),240        });241      }242    });243    return {success, tokens};244  }245246  static findCollectionInEvents(events: {event: IEvent}[], collectionId: number, expectedSection: string, expectedMethod: string): boolean {247    let eventId = null;248    events.forEach(({event: {data, method, section}}) => {249      if ((section === expectedSection) && (method === expectedMethod)) {250        eventId = parseInt(data[0].toString(), 10);251      }252    });253254    if (eventId === null) {255      throw Error(`No ${expectedMethod} event was found!`);256    }257    return eventId === collectionId;258  }259260  static isTokenTransferSuccess(events: {event: IEvent}[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {261    const normalizeAddress = (address: string | ICrossAccountId) => {262      if(typeof address === 'string') return address;263      const obj = {} as any;264      Object.keys(address).forEach(k => {265        obj[k.toLocaleLowerCase()] = address[k as 'Substrate' | 'Ethereum'];266      });267      if(obj.substrate) return CrossAccountId.withNormalizedSubstrate(obj.substrate);268      if(obj.ethereum) return CrossAccountId.fromLowerCaseKeys(obj).toLowerCase();269      return address;270    };271    let transfer = {collectionId: null, tokenId: null, from: null, to: null, amount: 1} as any;272    events.forEach(({event: {data, method, section}}) => {273      if ((section === 'common') && (method === 'Transfer')) {274        const hData = (data as any).toJSON();275        transfer = {276          collectionId: hData[0],277          tokenId: hData[1],278          from: normalizeAddress(hData[2]),279          to: normalizeAddress(hData[3]),280          amount: BigInt(hData[4]),281        };282      }283    });284    let isSuccess = parseInt(collectionId.toString()) === transfer.collectionId && parseInt(tokenId.toString()) === transfer.tokenId;285    isSuccess = isSuccess && JSON.stringify(normalizeAddress(fromAddressObj)) === JSON.stringify(transfer.from);286    isSuccess = isSuccess && JSON.stringify(normalizeAddress(toAddressObj)) === JSON.stringify(transfer.to);287    isSuccess = isSuccess && amount === transfer.amount;288    return isSuccess;289  }290291  static bigIntToDecimals(number: bigint, decimals = 18) {292    const numberStr = number.toString();293    const dotPos = numberStr.length - decimals;294295    if (dotPos <= 0) {296      return '0.' + '0'.repeat(Math.abs(dotPos)) + numberStr;297    } else {298      const intPart = numberStr.substring(0, dotPos);299      const fractPart = numberStr.substring(dotPos);300      return intPart + '.' + fractPart;301    }302  }303}304305class UniqueEventHelper {306  private static extractIndex(index: any): [number, number] | string {307    if(index.toRawType() === '[u8;2]') return [index[0], index[1]];308    return index.toJSON();309  }310311  private static extractSub(data: any, subTypes: any): {[key: string]: any} {312    let obj: any = {};313    let index = 0;314315    if (data.entries) {316      for(const [key, value] of data.entries()) {317        obj[key] = this.extractData(value, subTypes[index]);318        index++;319      }320    } else obj = data.toJSON();321322    return obj;323  }324325  private static toHuman(data: any) {326    return data && data.toHuman ? data.toHuman() : `${data}`;327  }328329  private static extractData(data: any, type: any): any {330    if(!type) return this.toHuman(data);331    if (['u16', 'u32'].indexOf(type.type) > -1) return data.toNumber();332    if (['u64', 'u128', 'u256'].indexOf(type.type) > -1) return data.toBigInt();333    if(type.hasOwnProperty('sub')) return this.extractSub(data, type.sub);334    return this.toHuman(data);335  }336337  public static extractEvents(events: {event: any, phase: any}[]): IEvent[] {338    const parsedEvents: IEvent[] = [];339340    events.forEach((record) => {341      const {event, phase} = record;342      const types = event.typeDef;343344      const eventData: IEvent = {345        section: event.section.toString(),346        method: event.method.toString(),347        index: this.extractIndex(event.index),348        data: [],349        phase: phase.toJSON(),350      };351352      event.data.forEach((val: any, index: number) => {353        eventData.data.push(this.extractData(val, types[index]));354      });355356      parsedEvents.push(eventData);357    });358359    return parsedEvents;360  }361}362363export class ChainHelperBase {364  helperBase: any;365366  transactionStatus = UniqueUtil.transactionStatus;367  chainLogType = UniqueUtil.chainLogType;368  util: typeof UniqueUtil;369  eventHelper: typeof UniqueEventHelper;370  logger: ILogger;371  api: ApiPromise | null;372  forcedNetwork: TNetworks | null;373  network: TNetworks | null;374  wsEndpoint: string | null;375  chainLog: IUniqueHelperLog[];376  children: ChainHelperBase[];377  address: AddressGroup;378  chain: ChainGroup;379380  constructor(logger?: ILogger, helperBase?: any) {381    this.helperBase = helperBase;382383    this.util = UniqueUtil;384    this.eventHelper = UniqueEventHelper;385    if (typeof logger == 'undefined') logger = this.util.getDefaultLogger();386    this.logger = logger;387    this.api = null;388    this.forcedNetwork = null;389    this.network = null;390    this.wsEndpoint = null;391    this.chainLog = [];392    this.children = [];393    this.address = new AddressGroup(this);394    this.chain = new ChainGroup(this);395  }396397  clone(helperCls: ChainHelperBaseConstructor, options: {[key: string]: any} = {}) {398    Object.setPrototypeOf(helperCls.prototype, this);399    const newHelper = new helperCls(this.logger, options);400401    newHelper.api = this.api;402    newHelper.network = this.network;403    newHelper.forceNetwork = this.forceNetwork;404405    this.children.push(newHelper);406407    return newHelper;408  }409410  getEndpoint(): string {411    if (this.wsEndpoint === null) throw Error('No connection was established');412    return this.wsEndpoint;413  }414415  getApi(): ApiPromise {416    if(this.api === null) throw Error('API not initialized');417    return this.api;418  }419420  async subscribeEvents(expectedEvents: {section: string, names: string[]}[]) {421    const collectedEvents: IEvent[] = [];422    const unsubscribe = await this.getApi().query.system.events((events: Vec<FrameSystemEventRecord>) => {423      const ievents = this.eventHelper.extractEvents(events);424      ievents.forEach((event) => {425        expectedEvents.forEach((e => {426          if (event.section === e.section && e.names.includes(event.method)) {427            collectedEvents.push(event);428          }429        }));430      });431    });432    return {unsubscribe: unsubscribe as any, collectedEvents};433  }434435  clearChainLog(): void {436    this.chainLog = [];437  }438439  forceNetwork(value: TNetworks): void {440    this.forcedNetwork = value;441  }442443  async connect(wsEndpoint: string, listeners?: IApiListeners) {444    if (this.api !== null) throw Error('Already connected');445    const {api, network} = await ChainHelperBase.createConnection(wsEndpoint, listeners, this.forcedNetwork);446    this.wsEndpoint = wsEndpoint;447    this.api = api;448    this.network = network;449  }450451  async disconnect() {452    for (const child of this.children) {453      child.clearApi();454    }455456    if (this.api === null) return;457    await this.api.disconnect();458    this.clearApi();459  }460461  clearApi() {462    this.api = null;463    this.network = null;464  }465466  static async detectNetwork(api: ApiPromise): Promise<TNetworks> {467    const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any;468    const xcmChains = ['rococo', 'westend', 'westmint', 'acala', 'karura', 'moonbeam', 'moonriver'];469470    if(xcmChains.indexOf(spec.specName) > -1) return spec.specName;471472    if(['quartz', 'unique'].indexOf(spec.specName) > -1) return spec.specName;473    return 'opal';474  }475476  static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise<TNetworks> {477    const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});478    await api.isReady;479480    const network = await this.detectNetwork(api);481482    await api.disconnect();483484    return network;485  }486487  static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TNetworks | null): Promise<{488    api: ApiPromise;489    network: TNetworks;490  }> {491    if(typeof network === 'undefined' || network === null) network = 'opal';492    const supportedRPC = {493      opal: {494        unique: require('@unique-nft/opal-testnet-types/definitions').unique.rpc,495      },496      quartz: {497        unique: require('@unique-nft/quartz-mainnet-types/definitions').unique.rpc,498      },499      unique: {500        unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc,501      },502      rococo: {},503      westend: {},504      moonbeam: {},505      moonriver: {},506      acala: {},507      karura: {},508      westmint: {},509    };510    if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint);511    const rpc = supportedRPC[network];512513    // TODO: investigate how to replace rpc in runtime514    // api._rpcCore.addUserInterfaces(rpc);515516    const api = new ApiPromise({provider: new WsProvider(wsEndpoint), rpc});517518    await api.isReadyOrError;519520    if (typeof listeners === 'undefined') listeners = {};521    for (const event of ['connected', 'disconnected', 'error', 'ready', 'decorated']) {522      if (!listeners.hasOwnProperty(event) || typeof listeners[event as TApiAllowedListeners] === 'undefined') continue;523      api.on(event as ApiInterfaceEvents, listeners[event as TApiAllowedListeners] as (...args: any[]) => any);524    }525526    return {api, network};527  }528529  getTransactionStatus(data: {events: {event: IEvent}[], status: any}) {530    const {events, status} = data;531    if (status.isReady) {532      return this.transactionStatus.NOT_READY;533    }534    if (status.isBroadcast) {535      return this.transactionStatus.NOT_READY;536    }537    if (status.isInBlock || status.isFinalized) {538      const errors = events.filter(e => e.event.method === 'ExtrinsicFailed');539      if (errors.length > 0) {540        return this.transactionStatus.FAIL;541      }542      if (events.filter(e => e.event.method === 'ExtrinsicSuccess').length > 0) {543        return this.transactionStatus.SUCCESS;544      }545    }546547    return this.transactionStatus.FAIL;548  }549550  signTransaction(sender: TSigner, transaction: any, options: Partial<SignerOptions> | null = null, label = 'transaction') {551    const sign = (callback: any) => {552      if(options !== null) return transaction.signAndSend(sender, options, callback);553      return transaction.signAndSend(sender, callback);554    };555    // eslint-disable-next-line no-async-promise-executor556    return new Promise(async (resolve, reject) => {557      try {558        const unsub = await sign((result: any) => {559          const status = this.getTransactionStatus(result);560561          if (status === this.transactionStatus.SUCCESS) {562            this.logger.log(`${label} successful`);563            unsub();564            resolve({result, status});565          } else if (status === this.transactionStatus.FAIL) {566            let moduleError = null;567568            if (result.hasOwnProperty('dispatchError')) {569              const dispatchError = result['dispatchError'];570571              if (dispatchError) {572                if (dispatchError.isModule) {573                  const modErr = dispatchError.asModule;574                  const errorMeta = dispatchError.registry.findMetaError(modErr);575576                  moduleError = `${errorMeta.section}.${errorMeta.name}`;577                } else {578                  moduleError = dispatchError.toHuman();579                }580              } else {581                this.logger.log(result, this.logger.level.ERROR);582              }583            }584585            this.logger.log(`Something went wrong with ${label}. Status: ${status}`, this.logger.level.ERROR);586            unsub();587            reject({status, moduleError, result});588          }589        });590      } catch (e) {591        this.logger.log(e, this.logger.level.ERROR);592        reject(e);593      }594    });595  }596597  async signTransactionWithoutSending(signer: TSigner, tx: any) {598    const api = this.getApi();599    const signingInfo = await api.derive.tx.signingInfo(signer.address);600601    tx.sign(signer, {602      blockHash: api.genesisHash,603      genesisHash: api.genesisHash,604      runtimeVersion: api.runtimeVersion,605      nonce: signingInfo.nonce,606    });607608    return tx.toHex();609  }610611  async getPaymentInfo(signer: TSigner, tx: any, len: number | null) {612    const api = this.getApi();613    const signingInfo = await api.derive.tx.signingInfo(signer.address);614615    // We need to sign the tx because616    // unsigned transactions does not have an inclusion fee617    tx.sign(signer, {618      blockHash: api.genesisHash,619      genesisHash: api.genesisHash,620      runtimeVersion: api.runtimeVersion,621      nonce: signingInfo.nonce,622    });623624    if (len === null) {625      return (await this.callRpc('api.rpc.payment.queryInfo', [tx.toHex()])) as RuntimeDispatchInfo;626    } else {627      return (await api.call.transactionPaymentApi.queryInfo(tx, len)) as RuntimeDispatchInfo;628    }629  }630631  constructApiCall(apiCall: string, params: any[]) {632    if(!apiCall.startsWith('api.')) throw Error(`Invalid api call: ${apiCall}`);633    let call = this.getApi() as any;634    for(const part of apiCall.slice(4).split('.')) {635      call = call[part];636    }637    return call(...params);638  }639640  async executeExtrinsic(sender: TSigner, extrinsic: string, params: any[], expectSuccess=true, options: Partial<SignerOptions>|null = null/*, failureMessage='expected success'*/) {641    if(this.api === null) throw Error('API not initialized');642    if(!extrinsic.startsWith('api.tx.')) throw Error(`${extrinsic} is not transaction`);643644    const startTime = (new Date()).getTime();645    let result: ITransactionResult;646    let events: IEvent[] = [];647    try {648      result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), options, extrinsic) as ITransactionResult;649      events = this.eventHelper.extractEvents(result.result.events);650    }651    catch(e) {652      if(!(e as object).hasOwnProperty('status')) throw e;653      result = e as ITransactionResult;654    }655656    const endTime = (new Date()).getTime();657658    const log = {659      executedAt: endTime,660      executionTime: endTime - startTime,661      type: this.chainLogType.EXTRINSIC,662      status: result.status,663      call: extrinsic,664      signer: this.getSignerAddress(sender),665      params,666    } as IUniqueHelperLog;667668    if(result.status !== this.transactionStatus.SUCCESS) {669      if (result.moduleError) log.moduleError = result.moduleError;670      else if (result.result.dispatchError) log.dispatchError = result.result.dispatchError;671    }672    if(events.length > 0) log.events = events;673674    this.chainLog.push(log);675676    if(expectSuccess && result.status !== this.transactionStatus.SUCCESS) {677      if (result.moduleError) throw Error(`${result.moduleError}`);678      else if (result.result.dispatchError) throw Error(JSON.stringify(result.result.dispatchError));679    }680    return result;681  }682683  async callRpc(rpc: string, params?: any[]) {684    if(typeof params === 'undefined') params = [];685    if(this.api === null) throw Error('API not initialized');686    if(!rpc.startsWith('api.rpc.') && !rpc.startsWith('api.query.')) throw Error(`${rpc} is not RPC call`);687688    const startTime = (new Date()).getTime();689    let result;690    let error = null;691    const log = {692      type: this.chainLogType.RPC,693      call: rpc,694      params,695    } as IUniqueHelperLog;696697    try {698      result = await this.constructApiCall(rpc, params);699    }700    catch(e) {701      error = e;702    }703704    const endTime = (new Date()).getTime();705706    log.executedAt = endTime;707    log.status = (error === null ? this.transactionStatus.SUCCESS : this.transactionStatus.FAIL) as 'Fail' | 'Success';708    log.executionTime = endTime - startTime;709710    this.chainLog.push(log);711712    if(error !== null) throw error;713714    return result;715  }716717  getSignerAddress(signer: IKeyringPair | string): string {718    if(typeof signer === 'string') return signer;719    return signer.address;720  }721722  fetchAllPalletNames(): string[] {723    if(this.api === null) throw Error('API not initialized');724    return this.api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase());725  }726727  fetchMissingPalletNames(requiredPallets: string[]): string[] {728    const palletNames = this.fetchAllPalletNames();729    return requiredPallets.filter(p => !palletNames.includes(p));730  }731}732733734class HelperGroup<T extends ChainHelperBase> {735  helper: T;736737  constructor(uniqueHelper: T) {738    this.helper = uniqueHelper;739  }740}741742743class CollectionGroup extends HelperGroup<UniqueHelper> {744  /**745 * Get number of blocks when sponsored transaction is available.746 *747 * @param collectionId ID of collection748 * @param tokenId ID of token749 * @param addressObj address for which the sponsorship is checked750 * @example await getTokenNextSponsored(1, 2, {Substrate: '5DfhbVfww7ThF8q6f3...'});751 * @returns number of blocks or null if sponsorship hasn't been set752 */753  async getTokenNextSponsored(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<number | null> {754    return (await this.helper.callRpc('api.rpc.unique.nextSponsored', [collectionId, addressObj, tokenId])).toJSON();755  }756757  /**758   * Get the number of created collections.759   *760   * @returns number of created collections761   */762  async getTotalCount(): Promise<number> {763    return (await this.helper.callRpc('api.rpc.unique.collectionStats')).created.toNumber();764  }765766  /**767   * Get information about the collection with additional data,768   * including the number of tokens it contains, its administrators,769   * the normalized address of the collection's owner, and decoded name and description.770   *771   * @param collectionId ID of collection772   * @example await getData(2)773   * @returns collection information object774   */775  async getData(collectionId: number): Promise<{776    id: number;777    name: string;778    description: string;779    tokensCount: number;780    admins: CrossAccountId[];781    normalizedOwner: TSubstrateAccount;782    raw: any783  } | null> {784    const collection = await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId]);785    const humanCollection = collection.toHuman(), collectionData = {786      id: collectionId, name: null, description: null, tokensCount: 0, admins: [],787      raw: humanCollection,788    } as any, jsonCollection = collection.toJSON();789    if (humanCollection === null) return null;790    collectionData.raw.limits = jsonCollection.limits;791    collectionData.raw.permissions = jsonCollection.permissions;792    collectionData.normalizedOwner = this.helper.address.normalizeSubstrate(collectionData.raw.owner);793    for (const key of ['name', 'description']) {794      collectionData[key] = this.helper.util.vec2str(humanCollection[key]);795    }796797    collectionData.tokensCount = (['RFT', 'NFT'].includes(humanCollection.mode))798      ? await this.helper[humanCollection.mode.toLocaleLowerCase() as 'nft' | 'rft'].getLastTokenId(collectionId)799      : 0;800    collectionData.admins = await this.getAdmins(collectionId);801802    return collectionData;803  }804805  /**806   * Get the addresses of the collection's administrators, optionally normalized.807   *808   * @param collectionId ID of collection809   * @param normalize whether to normalize the addresses to the default ss58 format810   * @example await getAdmins(1)811   * @returns array of administrators812   */813  async getAdmins(collectionId: number, normalize = false): Promise<CrossAccountId[]> {814    const admins = (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman();815816    return normalize817      ? admins.map((address: CrossAccountId) => address.withNormalizedSubstrate())818      : admins;819  }820821  /**822   * Get the addresses added to the collection allow-list, optionally normalized.823   * @param collectionId ID of collection824   * @param normalize whether to normalize the addresses to the default ss58 format825   * @example await getAllowList(1)826   * @returns array of allow-listed addresses827   */828  async getAllowList(collectionId: number, normalize = false): Promise<CrossAccountId[]> {829    const allowListed = (await this.helper.callRpc('api.rpc.unique.allowlist', [collectionId])).toHuman();830    return normalize831      ? allowListed.map((address: CrossAccountId) => address.withNormalizedSubstrate())832      : allowListed;833  }834835  /**836   * Get the effective limits of the collection instead of null for default values837   *838   * @param collectionId ID of collection839   * @example await getEffectiveLimits(2)840   * @returns object of collection limits841   */842  async getEffectiveLimits(collectionId: number): Promise<ICollectionLimits> {843    return (await this.helper.callRpc('api.rpc.unique.effectiveCollectionLimits', [collectionId])).toJSON();844  }845846  /**847   * Burns the collection if the signer has sufficient permissions and collection is empty.848   *849   * @param signer keyring of signer850   * @param collectionId ID of collection851   * @example await helper.collection.burn(aliceKeyring, 3);852   * @returns ```true``` if extrinsic success, otherwise ```false```853   */854  async burn(signer: TSigner, collectionId: number): Promise<boolean> {855    const result = await this.helper.executeExtrinsic(856      signer,857      'api.tx.unique.destroyCollection', [collectionId],858      true,859    );860861    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionDestroyed');862  }863864  /**865   * Sets the sponsor for the collection (Requires the Substrate address). Needs confirmation by the sponsor.866   *867   * @param signer keyring of signer868   * @param collectionId ID of collection869   * @param sponsorAddress Sponsor substrate address870   * @example setSponsor(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...")871   * @returns ```true``` if extrinsic success, otherwise ```false```872   */873  async setSponsor(signer: TSigner, collectionId: number, sponsorAddress: TSubstrateAccount): Promise<boolean> {874    const result = await this.helper.executeExtrinsic(875      signer,876      'api.tx.unique.setCollectionSponsor', [collectionId, sponsorAddress],877      true,878    );879880    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorSet');881  }882883  /**884   * Confirms consent to sponsor the collection on behalf of the signer.885   *886   * @param signer keyring of signer887   * @param collectionId ID of collection888   * @example confirmSponsorship(aliceKeyring, 10)889   * @returns ```true``` if extrinsic success, otherwise ```false```890   */891  async confirmSponsorship(signer: TSigner, collectionId: number): Promise<boolean> {892    const result = await this.helper.executeExtrinsic(893      signer,894      'api.tx.unique.confirmSponsorship', [collectionId],895      true,896    );897898    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'SponsorshipConfirmed');899  }900901  /**902   * Removes the sponsor of a collection, regardless if it consented or not.903   *904   * @param signer keyring of signer905   * @param collectionId ID of collection906   * @example removeSponsor(aliceKeyring, 10)907   * @returns ```true``` if extrinsic success, otherwise ```false```908   */909  async removeSponsor(signer: TSigner, collectionId: number): Promise<boolean> {910    const result = await this.helper.executeExtrinsic(911      signer,912      'api.tx.unique.removeCollectionSponsor', [collectionId],913      true,914    );915916    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorRemoved');917  }918919  /**920   * Sets the limits of the collection. At least one limit must be specified for a correct call.921   *922   * @param signer keyring of signer923   * @param collectionId ID of collection924   * @param limits collection limits object925   * @example926   * await setLimits(927   *   aliceKeyring,928   *   10,929   *   {930   *     sponsorTransferTimeout: 0,931   *     ownerCanDestroy: false932   *   }933   * )934   * @returns ```true``` if extrinsic success, otherwise ```false```935   */936  async setLimits(signer: TSigner, collectionId: number, limits: ICollectionLimits): Promise<boolean> {937    const result = await this.helper.executeExtrinsic(938      signer,939      'api.tx.unique.setCollectionLimits', [collectionId, limits],940      true,941    );942943    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionLimitSet');944  }945946  /**947   * Changes the owner of the collection to the new Substrate address.948   *949   * @param signer keyring of signer950   * @param collectionId ID of collection951   * @param ownerAddress substrate address of new owner952   * @example changeOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...")953   * @returns ```true``` if extrinsic success, otherwise ```false```954   */955  async changeOwner(signer: TSigner, collectionId: number, ownerAddress: TSubstrateAccount): Promise<boolean> {956    const result = await this.helper.executeExtrinsic(957      signer,958      'api.tx.unique.changeCollectionOwner', [collectionId, ownerAddress],959      true,960    );961962    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionOwnerChanged');963  }964965  /**966   * Adds a collection administrator.967   *968   * @param signer keyring of signer969   * @param collectionId ID of collection970   * @param adminAddressObj Administrator address (substrate or ethereum)971   * @example addAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."})972   * @returns ```true``` if extrinsic success, otherwise ```false```973   */974  async addAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {975    const result = await this.helper.executeExtrinsic(976      signer,977      'api.tx.unique.addCollectionAdmin', [collectionId, adminAddressObj],978      true,979    );980981    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminAdded');982  }983984  /**985   * Removes a collection administrator.986   *987   * @param signer keyring of signer988   * @param collectionId ID of collection989   * @param adminAddressObj Administrator address (substrate or ethereum)990   * @example removeAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."})991   * @returns ```true``` if extrinsic success, otherwise ```false```992   */993  async removeAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {994    const result = await this.helper.executeExtrinsic(995      signer,996      'api.tx.unique.removeCollectionAdmin', [collectionId, adminAddressObj],997      true,998    );9991000    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminRemoved');1001  }10021003  /**1004   * Check if user is in allow list.1005   *1006   * @param collectionId ID of collection1007   * @param user Account to check1008   * @example await getAdmins(1)1009   * @returns is user in allow list1010   */1011  async allowed(collectionId: number, user: ICrossAccountId): Promise<boolean> {1012    return (await this.helper.callRpc('api.rpc.unique.allowed', [collectionId, user])).toJSON();1013  }10141015  /**1016   * Adds an address to allow list1017   * @param signer keyring of signer1018   * @param collectionId ID of collection1019   * @param addressObj address to add to the allow list1020   * @returns ```true``` if extrinsic success, otherwise ```false```1021   */1022  async addToAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {1023    const result = await this.helper.executeExtrinsic(1024      signer,1025      'api.tx.unique.addToAllowList', [collectionId, addressObj],1026      true,1027    );10281029    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressAdded');1030  }10311032  /**1033   * Removes an address from allow list1034   *1035   * @param signer keyring of signer1036   * @param collectionId ID of collection1037   * @param addressObj address to remove from the allow list1038   * @returns ```true``` if extrinsic success, otherwise ```false```1039   */1040  async removeFromAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {1041    const result = await this.helper.executeExtrinsic(1042      signer,1043      'api.tx.unique.removeFromAllowList', [collectionId, addressObj],1044      true,1045    );10461047    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressRemoved');1048  }10491050  /**1051   * Sets onchain permissions for selected collection.1052   *1053   * @param signer keyring of signer1054   * @param collectionId ID of collection1055   * @param permissions collection permissions object1056   * @example setPermissions(aliceKeyring, 10, {access:'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true}});1057   * @returns ```true``` if extrinsic success, otherwise ```false```1058   */1059  async setPermissions(signer: TSigner, collectionId: number, permissions: ICollectionPermissions): Promise<boolean> {1060    const result = await this.helper.executeExtrinsic(1061      signer,1062      'api.tx.unique.setCollectionPermissions', [collectionId, permissions],1063      true,1064    );10651066    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPermissionSet');1067  }10681069  /**1070   * Enables nesting for selected collection. If `restricted` set, you can nest only tokens from specified collections.1071   *1072   * @param signer keyring of signer1073   * @param collectionId ID of collection1074   * @param permissions nesting permissions object1075   * @example enableNesting(aliceKeyring, 10, {collectionAdmin: true, tokenOwner: true});1076   * @returns ```true``` if extrinsic success, otherwise ```false```1077   */1078  async enableNesting(signer: TSigner, collectionId: number, permissions: INestingPermissions): Promise<boolean> {1079    return await this.setPermissions(signer, collectionId, {nesting: permissions});1080  }10811082  /**1083   * Disables nesting for selected collection.1084   *1085   * @param signer keyring of signer1086   * @param collectionId ID of collection1087   * @example disableNesting(aliceKeyring, 10);1088   * @returns ```true``` if extrinsic success, otherwise ```false```1089   */1090  async disableNesting(signer: TSigner, collectionId: number): Promise<boolean> {1091    return await this.setPermissions(signer, collectionId, {nesting: {tokenOwner: false, collectionAdmin: false}});1092  }10931094  /**1095   * Sets onchain properties to the collection.1096   *1097   * @param signer keyring of signer1098   * @param collectionId ID of collection1099   * @param properties array of property objects1100   * @example setProperties(aliceKeyring, 10, [{key: "gender", value: "male"}]);1101   * @returns ```true``` if extrinsic success, otherwise ```false```1102   */1103  async setProperties(signer: TSigner, collectionId: number, properties: IProperty[]): Promise<boolean> {1104    const result = await this.helper.executeExtrinsic(1105      signer,1106      'api.tx.unique.setCollectionProperties', [collectionId, properties],1107      true,1108    );11091110    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertySet');1111  }11121113  /**1114   * Get collection properties.1115   *1116   * @param collectionId ID of collection1117   * @param propertyKeys optionally filter the returned properties to only these keys1118   * @example getProperties(1219, ['location', 'date', 'time', 'isParadise']);1119   * @returns array of key-value pairs1120   */1121  async getProperties(collectionId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1122    return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman();1123  }11241125  async getPropertiesConsumedSpace(collectionId: number): Promise<number> {1126    const api = this.helper.getApi();1127    const props = (await api.query.common.collectionProperties(collectionId)).toJSON();11281129    return (props! as any).consumedSpace;1130  }11311132  async getCollectionOptions(collectionId: number) {1133    return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1134  }11351136  /**1137   * Deletes onchain properties from the collection.1138   *1139   * @param signer keyring of signer1140   * @param collectionId ID of collection1141   * @param propertyKeys array of property keys to delete1142   * @example deleteProperties(aliceKeyring, 10, ["gender", "age"]);1143   * @returns ```true``` if extrinsic success, otherwise ```false```1144   */1145  async deleteProperties(signer: TSigner, collectionId: number, propertyKeys: string[]): Promise<boolean> {1146    const result = await this.helper.executeExtrinsic(1147      signer,1148      'api.tx.unique.deleteCollectionProperties', [collectionId, propertyKeys],1149      true,1150    );11511152    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertyDeleted');1153  }11541155  /**1156   * Changes the owner of the token.1157   *1158   * @param signer keyring of signer1159   * @param collectionId ID of collection1160   * @param tokenId ID of token1161   * @param addressObj address of a new owner1162   * @param amount amount of tokens to be transfered. For NFT must be set to 1n1163   * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})1164   * @returns true if the token success, otherwise false1165   */1166  async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {1167    const result = await this.helper.executeExtrinsic(1168      signer,1169      'api.tx.unique.transfer', [addressObj, collectionId, tokenId, amount],1170      true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`,1171    );11721173    return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, {Substrate: typeof signer === 'string' ? signer : signer.address}, addressObj, amount);1174  }11751176  /**1177   *1178   * Change ownership of a token(s) on behalf of the owner.1179   *1180   * @param signer keyring of signer1181   * @param collectionId ID of collection1182   * @param tokenId ID of token1183   * @param fromAddressObj address on behalf of which the token will be sent1184   * @param toAddressObj new token owner1185   * @param amount amount of tokens to be transfered. For NFT must be set to 1n1186   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg"}, {Ethereum: "0x9F0583DbB85..."})1187   * @returns true if the token success, otherwise false1188   */1189  async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1190    const result = await this.helper.executeExtrinsic(1191      signer,1192      'api.tx.unique.transferFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],1193      true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`,1194    );1195    return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1196  }11971198  /**1199   *1200   * Destroys a concrete instance of NFT/RFT or burns a specified amount of fungible tokens.1201   *1202   * @param signer keyring of signer1203   * @param collectionId ID of collection1204   * @param tokenId ID of token1205   * @param amount amount of tokens to be burned. For NFT must be set to 1n1206   * @example burnToken(aliceKeyring, 10, 5);1207   * @returns ```true``` if the extrinsic is successful, otherwise ```false```1208   */1209  async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1210    const burnResult = await this.helper.executeExtrinsic(1211      signer,1212      'api.tx.unique.burnItem', [collectionId, tokenId, amount],1213      true, // `Unable to burn token for ${label}`,1214    );1215    const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1216    if (burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens');1217    return burnedTokens.success;1218  }12191220  /**1221   * Destroys a concrete instance of NFT on behalf of the owner1222   *1223   * @param signer keyring of signer1224   * @param collectionId ID of collection1225   * @param tokenId ID of token1226   * @param fromAddressObj address on behalf of which the token will be burnt1227   * @param amount amount of tokens to be burned. For NFT must be set to 1n1228   * @example burnTokenFrom(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}, 5, {Ethereum: "0x9F0583DbB85..."})1229   * @returns ```true``` if extrinsic success, otherwise ```false```1230   */1231  async burnTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1232    const burnResult = await this.helper.executeExtrinsic(1233      signer,1234      'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount],1235      true, // `Unable to burn token from for ${label}`,1236    );1237    const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1238    return burnedTokens.success && burnedTokens.tokens.length > 0;1239  }12401241  /**1242   * Set, change, or remove approved address to transfer the ownership of the NFT.1243   *1244   * @param signer keyring of signer1245   * @param collectionId ID of collection1246   * @param tokenId ID of token1247   * @param toAddressObj Substrate or Ethereum address which gets approved use of the signer's tokens1248   * @param amount amount of token to be approved. For NFT must be set to 1n1249   * @returns ```true``` if extrinsic success, otherwise ```false```1250   */1251  async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1252    const approveResult = await this.helper.executeExtrinsic(1253      signer,1254      'api.tx.unique.approve', [toAddressObj, collectionId, tokenId, amount],1255      true, // `Unable to approve token for ${label}`,1256    );12571258    return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved');1259  }12601261  /**1262   * Get the amount of token pieces approved to transfer or burn. Normally 0.1263   *1264   * @param collectionId ID of collection1265   * @param tokenId ID of token1266   * @param toAccountObj address which is approved to use token pieces1267   * @param fromAccountObj address which may have allowed the use of its owned tokens1268   * @example getTokenApprovedPieces(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5ERZNF88Mm7UGfPP3mdG..."})1269   * @returns number of approved to transfer pieces1270   */1271  async getTokenApprovedPieces(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId, fromAccountObj: ICrossAccountId): Promise<bigint> {1272    return (await this.helper.callRpc('api.rpc.unique.allowance', [collectionId, fromAccountObj, toAccountObj, tokenId])).toBigInt();1273  }12741275  /**1276   * Get the last created token ID in a collection1277   *1278   * @param collectionId ID of collection1279   * @example getLastTokenId(10);1280   * @returns id of the last created token1281   */1282  async getLastTokenId(collectionId: number): Promise<number> {1283    return (await this.helper.callRpc('api.rpc.unique.lastTokenId', [collectionId])).toNumber();1284  }12851286  /**1287   * Check if token exists1288   *1289   * @param collectionId ID of collection1290   * @param tokenId ID of token1291   * @example doesTokenExist(10, 20);1292   * @returns true if the token exists, otherwise false1293   */1294  async doesTokenExist(collectionId: number, tokenId: number): Promise<boolean> {1295    return (await this.helper.callRpc('api.rpc.unique.tokenExists', [collectionId, tokenId])).toJSON();1296  }1297}12981299class NFTnRFT extends CollectionGroup {1300  /**1301   * Get tokens owned by account1302   *1303   * @param collectionId ID of collection1304   * @param addressObj tokens owner1305   * @example getTokensByAddress(10, {Substrate: "5DyN4Y92vZCjv38fg..."})1306   * @returns array of token ids owned by account1307   */1308  async getTokensByAddress(collectionId: number, addressObj: ICrossAccountId): Promise<number[]> {1309    return (await this.helper.callRpc('api.rpc.unique.accountTokens', [collectionId, addressObj])).toJSON();1310  }13111312  /**1313   * Get token data1314   *1315   * @param collectionId ID of collection1316   * @param tokenId ID of token1317   * @param propertyKeys optionally filter the token properties to only these keys1318   * @param blockHashAt optionally query the data at some block with this hash1319   * @example getToken(10, 5);1320   * @returns human readable token data1321   */1322  async getToken(collectionId: number, tokenId: number, propertyKeys: string[] = [], blockHashAt?: string): Promise<{1323    properties: IProperty[];1324    owner: CrossAccountId;1325    normalizedOwner: CrossAccountId;1326  }| null> {1327    let tokenData;1328    if(typeof blockHashAt === 'undefined') {1329      tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId]);1330    }1331    else {1332      if(propertyKeys.length == 0) {1333        const collection = (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1334        if(!collection) return null;1335        propertyKeys = collection.tokenPropertyPermissions.map((x: ITokenPropertyPermission) => x.key);1336      }1337      tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId, propertyKeys, blockHashAt]);1338    }1339    tokenData = tokenData.toHuman();1340    if (tokenData === null || tokenData.owner === null) return null;1341    const owner = {} as any;1342    for (const key of Object.keys(tokenData.owner)) {1343      owner[key.toLocaleLowerCase()] = key.toLocaleLowerCase() == 'substrate'1344        ? CrossAccountId.normalizeSubstrateAddress(tokenData.owner[key])1345        : tokenData.owner[key];1346    }1347    tokenData.normalizedOwner = CrossAccountId.fromLowerCaseKeys(owner);1348    return tokenData;1349  }13501351  /**1352   * Set permissions to change token properties1353   *1354   * @param signer keyring of signer1355   * @param collectionId ID of collection1356   * @param permissions permissions to change a property by the collection admin or token owner1357   * @example setTokenPropertyPermissions(1358   *   aliceKeyring, 10, [{key: "gender", permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}]1359   * )1360   * @returns true if extrinsic success otherwise false1361   */1362  async setTokenPropertyPermissions(signer: TSigner, collectionId: number, permissions: ITokenPropertyPermission[]): Promise<boolean> {1363    const result = await this.helper.executeExtrinsic(1364      signer,1365      'api.tx.unique.setTokenPropertyPermissions', [collectionId, permissions],1366      true,1367    );13681369    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'PropertyPermissionSet');1370  }13711372  /**1373   * Get token property permissions.1374   *1375   * @param collectionId ID of collection1376   * @param propertyKeys optionally filter the returned property permissions to only these keys1377   * @example getPropertyPermissions(1219, ['location', 'date', 'time', 'isParadise']);1378   * @returns array of key-permission pairs1379   */1380  async getPropertyPermissions(collectionId: number, propertyKeys: string[] | null = null): Promise<ITokenPropertyPermission[]> {1381    return (await this.helper.callRpc('api.rpc.unique.propertyPermissions', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();1382  }13831384  /**1385   * Set token properties1386   *1387   * @param signer keyring of signer1388   * @param collectionId ID of collection1389   * @param tokenId ID of token1390   * @param properties key-value pairs of metadata which to add to a token. Keys must be permitted in the collection1391   * @example setTokenProperties(aliceKeyring, 10, 5, [{key: "gender", value: "female"}, {key: "age", value: "23"}])1392   * @returns ```true``` if extrinsic success, otherwise ```false```1393   */1394  async setTokenProperties(signer: TSigner, collectionId: number, tokenId: number, properties: IProperty[]): Promise<boolean> {1395    const result = await this.helper.executeExtrinsic(1396      signer,1397      'api.tx.unique.setTokenProperties', [collectionId, tokenId, properties],1398      true,1399    );14001401    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertySet');1402  }14031404  /**1405   * Get properties, metadata assigned to a token.1406   *1407   * @param collectionId ID of collection1408   * @param tokenId ID of token1409   * @param propertyKeys optionally filter the returned properties to only these keys1410   * @example getTokenProperties(1219, ['location', 'date', 'time', 'isParadise']);1411   * @returns array of key-value pairs1412   */1413  async getTokenProperties(collectionId: number, tokenId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1414    return (await this.helper.callRpc('api.rpc.unique.tokenProperties', [collectionId, tokenId, propertyKeys])).toHuman();1415  }14161417  /**1418   * Delete the provided properties of a token1419   * @param signer keyring of signer1420   * @param collectionId ID of collection1421   * @param tokenId ID of token1422   * @param propertyKeys property keys to be deleted1423   * @example deleteTokenProperties(aliceKeyring, 10, 5, ["gender", "age"])1424   * @returns ```true``` if extrinsic success, otherwise ```false```1425   */1426  async deleteTokenProperties(signer: TSigner, collectionId: number, tokenId: number, propertyKeys: string[]): Promise<boolean> {1427    const result = await this.helper.executeExtrinsic(1428      signer,1429      'api.tx.unique.deleteTokenProperties', [collectionId, tokenId, propertyKeys],1430      true,1431    );14321433    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertyDeleted');1434  }14351436  /**1437   * Mint new collection1438   *1439   * @param signer keyring of signer1440   * @param collectionOptions basic collection options and properties1441   * @param mode NFT or RFT type of a collection1442   * @example mintCollection(aliceKeyring, {name: 'New', description: "New collection", tokenPrefix: "NEW"}, "NFT")1443   * @returns object of the created collection1444   */1445  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise<UniqueBaseCollection> {1446    collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object1447    collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null};1448    for (const key of ['name', 'description', 'tokenPrefix']) {1449      if (typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string);1450    }1451    const creationResult = await this.helper.executeExtrinsic(1452      signer,1453      'api.tx.unique.createCollectionEx', [collectionOptions],1454      true, // errorLabel,1455    );1456    return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));1457  }14581459  getCollectionObject(_collectionId: number): any {1460    return null;1461  }14621463  getTokenObject(_collectionId: number, _tokenId: number): any {1464    return null;1465  }14661467  /**1468   * Tells whether the given `owner` approves the `operator`.1469   * @param collectionId ID of collection1470   * @param owner owner address1471   * @param operator operator addrees1472   * @returns true if operator is enabled1473   */1474  async allowanceForAll(collectionId: number, owner: ICrossAccountId, operator: ICrossAccountId): Promise<boolean> {1475    return (await this.helper.callRpc('api.rpc.unique.allowanceForAll', [collectionId, owner, operator])).toJSON();1476  }14771478  /** Sets or unsets the approval of a given operator.1479   *  The `operator` is allowed to transfer all tokens of the `caller` on their behalf.1480   *  @param operator Operator1481   *  @param approved Should operator status be granted or revoked?1482   *  @returns ```true``` if extrinsic success, otherwise ```false```1483   */1484  async setAllowanceForAll(signer: TSigner, collectionId: number, operator: ICrossAccountId, approved: boolean): Promise<boolean> {1485    const result = await this.helper.executeExtrinsic(1486      signer,1487      'api.tx.unique.setAllowanceForAll', [collectionId, operator, approved],1488      true,1489    );1490    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'ApprovedForAll');1491  }1492}149314941495class NFTGroup extends NFTnRFT {1496  /**1497   * Get collection object1498   * @param collectionId ID of collection1499   * @example getCollectionObject(2);1500   * @returns instance of UniqueNFTCollection1501   */1502  getCollectionObject(collectionId: number): UniqueNFTCollection {1503    return new UniqueNFTCollection(collectionId, this.helper);1504  }15051506  /**1507   * Get token object1508   * @param collectionId ID of collection1509   * @param tokenId ID of token1510   * @example getTokenObject(10, 5);1511   * @returns instance of UniqueNFTToken1512   */1513  getTokenObject(collectionId: number, tokenId: number): UniqueNFToken {1514    return new UniqueNFToken(tokenId, this.getCollectionObject(collectionId));1515  }15161517  /**1518   * Get token's owner1519   * @param collectionId ID of collection1520   * @param tokenId ID of token1521   * @param blockHashAt optionally query the data at the block with this hash1522   * @example getTokenOwner(10, 5);1523   * @returns Address in CrossAccountId format, e.g. {Substrate: "5DnSF6RRjwteE3BrCj..."}1524   */1525  async getTokenOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId> {1526    let owner;1527    if (typeof blockHashAt === 'undefined') {1528      owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId]);1529    } else {1530      owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId, blockHashAt]);1531    }1532    return CrossAccountId.fromLowerCaseKeys(owner.toJSON());1533  }15341535  /**1536   * Is token approved to transfer1537   * @param collectionId ID of collection1538   * @param tokenId ID of token1539   * @param toAccountObj address to be approved1540   * @returns ```true``` if extrinsic success, otherwise ```false```1541   */1542  async isTokenApproved(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId): Promise<boolean> {1543    return (await this.getTokenApprovedPieces(collectionId, tokenId, toAccountObj, await this.getTokenOwner(collectionId, tokenId))) === 1n;1544  }15451546  /**1547   * Changes the owner of the token.1548   *1549   * @param signer keyring of signer1550   * @param collectionId ID of collection1551   * @param tokenId ID of token1552   * @param addressObj address of a new owner1553   * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})1554   * @returns ```true``` if extrinsic success, otherwise ```false```1555   */1556  async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<boolean> {1557    return await super.transferToken(signer, collectionId, tokenId, addressObj, 1n);1558  }15591560  /**1561   *1562   * Change ownership of a NFT on behalf of the owner.1563   *1564   * @param signer keyring of signer1565   * @param collectionId ID of collection1566   * @param tokenId ID of token1567   * @param fromAddressObj address on behalf of which the token will be sent1568   * @param toAddressObj new token owner1569   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Ethereum: "0x9F0583DbB85..."})1570   * @returns ```true``` if extrinsic success, otherwise ```false```1571   */1572  async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId): Promise<boolean> {1573    return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, 1n);1574  }15751576  /**1577   * Recursively find the address that owns the token1578   * @param collectionId ID of collection1579   * @param tokenId ID of token1580   * @param blockHashAt1581   * @example getTokenTopmostOwner(10, 5);1582   * @returns address in CrossAccountId format, e.g. {Substrate: "5DyN4Y92vZCjv38fg..."}1583   */1584  async getTokenTopmostOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId | null> {1585    let owner;1586    if (typeof blockHashAt === 'undefined') {1587      owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId]);1588    } else {1589      owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId, blockHashAt]);1590    }15911592    if (owner === null) return null;15931594    return owner.toHuman();1595  }15961597  /**1598   * Get tokens nested in the provided token1599   * @param collectionId ID of collection1600   * @param tokenId ID of token1601   * @param blockHashAt optionally query the data at the block with this hash1602   * @example getTokenChildren(10, 5);1603   * @returns tokens whose depth of nesting is <= 51604   */1605  async getTokenChildren(collectionId: number, tokenId: number, blockHashAt?: string): Promise<IToken[]> {1606    let children;1607    if(typeof blockHashAt === 'undefined') {1608      children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId]);1609    } else {1610      children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId, blockHashAt]);1611    }16121613    return children.toJSON().map((x: any) => {1614      return {collectionId: x.collection, tokenId: x.token};1615    });1616  }16171618  /**1619   * Nest one token into another1620   * @param signer keyring of signer1621   * @param tokenObj token to be nested1622   * @param rootTokenObj token to be parent1623   * @example nestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4});1624   * @returns ```true``` if extrinsic success, otherwise ```false```1625   */1626  async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken): Promise<boolean> {1627    const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1628    const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress);1629    if(!result) {1630      throw Error('Unable to nest token!');1631    }1632    return result;1633  }16341635  /**1636   * Remove token from nested state1637   * @param signer keyring of signer1638   * @param tokenObj token to unnest1639   * @param rootTokenObj parent of a token1640   * @param toAddressObj address of a new token owner1641   * @example unnestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4}, {Substrate: "5DyN4Y92vZCjv38fg..."});1642   * @returns ```true``` if extrinsic success, otherwise ```false```1643   */1644  async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId): Promise<boolean> {1645    const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1646    const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj);1647    if(!result) {1648      throw Error('Unable to unnest token!');1649    }1650    return result;1651  }16521653  /**1654   * Mint new collection1655   * @param signer keyring of signer1656   * @param collectionOptions Collection options1657   * @example1658   * mintCollection(aliceKeyring, {1659   *   name: 'New',1660   *   description: 'New collection',1661   *   tokenPrefix: 'NEW',1662   * })1663   * @returns object of the created collection1664   */1665  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueNFTCollection> {1666    return await super.mintCollection(signer, collectionOptions, 'NFT') as UniqueNFTCollection;1667  }16681669  /**1670   * Mint new token1671   * @param signer keyring of signer1672   * @param data token data1673   * @returns created token object1674   */1675  async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }): Promise<UniqueNFToken> {1676    const creationResult = await this.helper.executeExtrinsic(1677      signer,1678      'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1679        nft: {1680          properties: data.properties,1681        },1682      }],1683      true,1684    );1685    const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1686    if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1687    if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1688    return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1689  }16901691  /**1692   * Mint multiple NFT tokens1693   * @param signer keyring of signer1694   * @param collectionId ID of collection1695   * @param tokens array of tokens with owner and properties1696   * @example1697   * mintMultipleTokens(aliceKeyring, 10, [{1698   *     owner: {Substrate: "5DyN4Y92vZCjv38fg..."},1699   *     properties: [{key: "gender", value: "male"},{key: "age", value: "45"}],1700   *   },{1701   *     owner: {Ethereum: "0x9F0583DbB855d..."},1702   *     properties: [{key: "gender", value: "female"},{key: "age", value: "22"}],1703   * }]);1704   * @returns ```true``` if extrinsic success, otherwise ```false```1705   */1706  async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1707    const creationResult = await this.helper.executeExtrinsic(1708      signer,1709      'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}],1710      true,1711    );1712    const collection = this.getCollectionObject(collectionId);1713    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1714  }17151716  /**1717   * Mint multiple NFT tokens with one owner1718   * @param signer keyring of signer1719   * @param collectionId ID of collection1720   * @param owner tokens owner1721   * @param tokens array of tokens with owner and properties1722   * @example1723   * mintMultipleTokensWithOneOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...", [{1724   *   properties: [{1725   *   key: "gender",1726   *   value: "female",1727   *  },{1728   *   key: "age",1729   *   value: "33",1730   *  }],1731   * }]);1732   * @returns array of newly created tokens1733   */1734  async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1735    const rawTokens = [];1736    for (const token of tokens) {1737      const raw = {NFT: {properties: token.properties}};1738      rawTokens.push(raw);1739    }1740    const creationResult = await this.helper.executeExtrinsic(1741      signer,1742      'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1743      true,1744    );1745    const collection = this.getCollectionObject(collectionId);1746    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1747  }17481749  /**1750   * Set, change, or remove approved address to transfer the ownership of the NFT.1751   *1752   * @param signer keyring of signer1753   * @param collectionId ID of collection1754   * @param tokenId ID of token1755   * @param toAddressObj address to approve1756   * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})1757   * @returns ```true``` if extrinsic success, otherwise ```false```1758   */1759  approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId) {1760    return super.approveToken(signer, collectionId, tokenId, toAddressObj, 1n);1761  }1762}176317641765class RFTGroup extends NFTnRFT {1766  /**1767   * Get collection object1768   * @param collectionId ID of collection1769   * @example getCollectionObject(2);1770   * @returns instance of UniqueRFTCollection1771   */1772  getCollectionObject(collectionId: number): UniqueRFTCollection {1773    return new UniqueRFTCollection(collectionId, this.helper);1774  }17751776  /**1777   * Get token object1778   * @param collectionId ID of collection1779   * @param tokenId ID of token1780   * @example getTokenObject(10, 5);1781   * @returns instance of UniqueNFTToken1782   */1783  getTokenObject(collectionId: number, tokenId: number): UniqueRFToken {1784    return new UniqueRFToken(tokenId, this.getCollectionObject(collectionId));1785  }17861787  /**1788   * Get top 10 token owners with the largest number of pieces1789   * @param collectionId ID of collection1790   * @param tokenId ID of token1791   * @example getTokenTop10Owners(10, 5);1792   * @returns array of top 10 owners1793   */1794  async getTokenTop10Owners(collectionId: number, tokenId: number): Promise<CrossAccountId[]> {1795    return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, tokenId])).toJSON().map(CrossAccountId.fromLowerCaseKeys);1796  }17971798  /**1799   * Get number of pieces owned by address1800   * @param collectionId ID of collection1801   * @param tokenId ID of token1802   * @param addressObj address token owner1803   * @example getTokenBalance(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."});1804   * @returns number of pieces ownerd by address1805   */1806  async getTokenBalance(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<bigint> {1807    return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, tokenId])).toBigInt();1808  }18091810  /**1811   * Transfer pieces of token to another address1812   * @param signer keyring of signer1813   * @param collectionId ID of collection1814   * @param tokenId ID of token1815   * @param addressObj address of a new owner1816   * @param amount number of pieces to be transfered1817   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2000n)1818   * @returns ```true``` if extrinsic success, otherwise ```false```1819   */1820  async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {1821    return await super.transferToken(signer, collectionId, tokenId, addressObj, amount);1822  }18231824  /**1825   * Change ownership of some pieces of RFT on behalf of the owner.1826   * @param signer keyring of signer1827   * @param collectionId ID of collection1828   * @param tokenId ID of token1829   * @param fromAddressObj address on behalf of which the token will be sent1830   * @param toAddressObj new token owner1831   * @param amount number of pieces to be transfered1832   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5DfhbVfww7ThF8q6f3i..."}, 2000n)1833   * @returns ```true``` if extrinsic success, otherwise ```false```1834   */1835  async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1836    return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1837  }18381839  /**1840   * Mint new collection1841   * @param signer keyring of signer1842   * @param collectionOptions Collection options1843   * @example1844   * mintCollection(aliceKeyring, {1845   *   name: 'New',1846   *   description: 'New collection',1847   *   tokenPrefix: 'NEW',1848   * })1849   * @returns object of the created collection1850   */1851  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueRFTCollection> {1852    return await super.mintCollection(signer, collectionOptions, 'RFT') as UniqueRFTCollection;1853  }18541855  /**1856   * Mint new token1857   * @param signer keyring of signer1858   * @param data token data1859   * @example mintToken(aliceKeyring, {collectionId: 10, owner: {Substrate: '5GHoZe9c73RYbVzq...'}, pieces: 10000n});1860   * @returns created token object1861   */1862  async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }): Promise<UniqueRFToken> {1863    const creationResult = await this.helper.executeExtrinsic(1864      signer,1865      'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1866        refungible: {1867          pieces: data.pieces,1868          properties: data.properties,1869        },1870      }],1871      true,1872    );1873    const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1874    if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1875    if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1876    return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1877  }18781879  async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1880    throw Error('Not implemented');1881    const creationResult = await this.helper.executeExtrinsic(1882      signer,1883      'api.tx.unique.createMultipleItemsEx', [collectionId, {RefungibleMultipleOwners: tokens}],1884      true, // `Unable to mint RFT tokens for ${label}`,1885    );1886    const collection = this.getCollectionObject(collectionId);1887    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1888  }18891890  /**1891   * Mint multiple RFT tokens with one owner1892   * @param signer keyring of signer1893   * @param collectionId ID of collection1894   * @param owner tokens owner1895   * @param tokens array of tokens with properties and pieces1896   * @example mintMultipleTokensWithOneOwner(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, [{pieces: 100000n, properties: [{key: "gender", value: "male"}]}]);1897   * @returns array of newly created RFT tokens1898   */1899  async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1900    const rawTokens = [];1901    for (const token of tokens) {1902      const raw = {ReFungible: {pieces: token.pieces, properties: token.properties}};1903      rawTokens.push(raw);1904    }1905    const creationResult = await this.helper.executeExtrinsic(1906      signer,1907      'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1908      true,1909    );1910    const collection = this.getCollectionObject(collectionId);1911    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1912  }19131914  /**1915   * Destroys a concrete instance of RFT.1916   * @param signer keyring of signer1917   * @param collectionId ID of collection1918   * @param tokenId ID of token1919   * @param amount number of pieces to be burnt1920   * @example burnToken(aliceKeyring, 10, 5);1921   * @returns ```true``` if the extrinsic is successful, otherwise ```false```1922   */1923  async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1924    return await super.burnToken(signer, collectionId, tokenId, amount);1925  }19261927  /**1928   * Destroys a concrete instance of RFT on behalf of the owner.1929   * @param signer keyring of signer1930   * @param collectionId ID of collection1931   * @param tokenId ID of token1932   * @param fromAddressObj address on behalf of which the token will be burnt1933   * @param amount number of pieces to be burnt1934   * @example burnTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2n)1935   * @returns ```true``` if extrinsic success, otherwise ```false```1936   */1937  async burnTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1938    return await super.burnTokenFrom(signer, collectionId, tokenId, fromAddressObj, amount);1939  }19401941  /**1942   * Set, change, or remove approved address to transfer the ownership of the RFT.1943   *1944   * @param signer keyring of signer1945   * @param collectionId ID of collection1946   * @param tokenId ID of token1947   * @param toAddressObj address to approve1948   * @param amount number of pieces to be approved1949   * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5GHoZe9c73RYbVzq..."}, "", 10000n);1950   * @returns true if the token success, otherwise false1951   */1952  approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1953    return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount);1954  }19551956  /**1957   * Get total number of pieces1958   * @param collectionId ID of collection1959   * @param tokenId ID of token1960   * @example getTokenTotalPieces(10, 5);1961   * @returns number of pieces1962   */1963  async getTokenTotalPieces(collectionId: number, tokenId: number): Promise<bigint> {1964    return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, tokenId])).unwrap().toBigInt();1965  }19661967  /**1968   * Change number of token pieces. Signer must be the owner of all token pieces.1969   * @param signer keyring of signer1970   * @param collectionId ID of collection1971   * @param tokenId ID of token1972   * @param amount new number of pieces1973   * @example repartitionToken(aliceKeyring, 10, 5, 12345n);1974   * @returns true if the repartion was success, otherwise false1975   */1976  async repartitionToken(signer: TSigner, collectionId: number, tokenId: number, amount: bigint): Promise<boolean> {1977    const currentAmount = await this.getTokenTotalPieces(collectionId, tokenId);1978    const repartitionResult = await this.helper.executeExtrinsic(1979      signer,1980      'api.tx.unique.repartition', [collectionId, tokenId, amount],1981      true,1982    );1983    if(currentAmount < amount) return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemCreated');1984    return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemDestroyed');1985  }1986}198719881989class FTGroup extends CollectionGroup {1990  /**1991   * Get collection object1992   * @param collectionId ID of collection1993   * @example getCollectionObject(2);1994   * @returns instance of UniqueFTCollection1995   */1996  getCollectionObject(collectionId: number): UniqueFTCollection {1997    return new UniqueFTCollection(collectionId, this.helper);1998  }19992000  /**2001   * Mint new fungible collection2002   * @param signer keyring of signer2003   * @param collectionOptions Collection options2004   * @param decimalPoints number of token decimals2005   * @example2006   * mintCollection(aliceKeyring, {2007   *   name: 'New',2008   *   description: 'New collection',2009   *   tokenPrefix: 'NEW',2010   * }, 18)2011   * @returns newly created fungible collection2012   */2013  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}, decimalPoints = 0): Promise<UniqueFTCollection> {2014    collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object2015    if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions');2016    collectionOptions.mode = {fungible: decimalPoints};2017    for (const key of ['name', 'description', 'tokenPrefix']) {2018      if (typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string);2019    }2020    const creationResult = await this.helper.executeExtrinsic(2021      signer,2022      'api.tx.unique.createCollectionEx', [collectionOptions],2023      true,2024    );2025    return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));2026  }20272028  /**2029   * Mint tokens2030   * @param signer keyring of signer2031   * @param collectionId ID of collection2032   * @param owner address owner of new tokens2033   * @param amount amount of tokens to be meanted2034   * @example mintTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq"}, 1000n);2035   * @returns ```true``` if extrinsic success, otherwise ```false```2036   */2037  async mintTokens(signer: TSigner, collectionId: number, amount: bigint, owner: ICrossAccountId | string): Promise<boolean> {2038    const creationResult = await this.helper.executeExtrinsic(2039      signer,2040      'api.tx.unique.createItem', [collectionId, (typeof owner === 'string') ? {Substrate: owner} : owner, {2041        fungible: {2042          value: amount,2043        },2044      }],2045      true, // `Unable to mint fungible tokens for ${label}`,2046    );2047    return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');2048  }20492050  /**2051   * Mint multiple Fungible tokens with one owner2052   * @param signer keyring of signer2053   * @param collectionId ID of collection2054   * @param owner tokens owner2055   * @param tokens array of tokens with properties and pieces2056   * @returns ```true``` if extrinsic success, otherwise ```false```2057   */2058  async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, tokens: {value: bigint}[], owner: ICrossAccountId): Promise<boolean> {2059    const rawTokens = [];2060    for (const token of tokens) {2061      const raw = {Fungible: {Value: token.value}};2062      rawTokens.push(raw);2063    }2064    const creationResult = await this.helper.executeExtrinsic(2065      signer,2066      'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],2067      true,2068    );2069    return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');2070  }20712072  /**2073   * Get the top 10 owners with the largest balance for the Fungible collection2074   * @param collectionId ID of collection2075   * @example getTop10Owners(10);2076   * @returns array of ```ICrossAccountId```2077   */2078  async getTop10Owners(collectionId: number): Promise<CrossAccountId[]> {2079    return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, 0])).toJSON().map(CrossAccountId.fromLowerCaseKeys);2080  }20812082  /**2083   * Get account balance2084   * @param collectionId ID of collection2085   * @param addressObj address of owner2086   * @example getBalance(10, {Substrate: "5GHoZe9c73RYbVzq..."})2087   * @returns amount of fungible tokens owned by address2088   */2089  async getBalance(collectionId: number, addressObj: ICrossAccountId): Promise<bigint> {2090    return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, 0])).toBigInt();2091  }20922093  /**2094   * Transfer tokens to address2095   * @param signer keyring of signer2096   * @param collectionId ID of collection2097   * @param toAddressObj address recipient2098   * @param amount amount of tokens to be sent2099   * @example transfer(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n);2100   * @returns ```true``` if extrinsic success, otherwise ```false```2101   */2102  async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {2103    return await super.transferToken(signer, collectionId, 0, toAddressObj, amount);2104  }21052106  /**2107   * Transfer some tokens on behalf of the owner.2108   * @param signer keyring of signer2109   * @param collectionId ID of collection2110   * @param fromAddressObj address on behalf of which tokens will be sent2111   * @param toAddressObj address where token to be sent2112   * @param amount number of tokens to be sent2113   * @example transferFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, {Substrate: "5DfhbVfww7ThF8q6f3ij..."}, 10000n);2114   * @returns ```true``` if extrinsic success, otherwise ```false```2115   */2116  async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {2117    return await super.transferTokenFrom(signer, collectionId, 0, fromAddressObj, toAddressObj, amount);2118  }21192120  /**2121   * Destroy some amount of tokens2122   * @param signer keyring of signer2123   * @param collectionId ID of collection2124   * @param amount amount of tokens to be destroyed2125   * @example burnTokens(aliceKeyring, 10, 1000n);2126   * @returns ```true``` if extrinsic success, otherwise ```false```2127   */2128  async burnTokens(signer: IKeyringPair, collectionId: number, amount=1n): Promise<boolean> {2129    return await super.burnToken(signer, collectionId, 0, amount);2130  }21312132  /**2133   * Burn some tokens on behalf of the owner.2134   * @param signer keyring of signer2135   * @param collectionId ID of collection2136   * @param fromAddressObj address on behalf of which tokens will be burnt2137   * @param amount amount of tokens to be burnt2138   * @example burnTokensFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n);2139   * @returns ```true``` if extrinsic success, otherwise ```false```2140   */2141  async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {2142    return await super.burnTokenFrom(signer, collectionId, 0, fromAddressObj, amount);2143  }21442145  /**2146   * Get total collection supply2147   * @param collectionId2148   * @returns2149   */2150  async getTotalPieces(collectionId: number): Promise<bigint> {2151    return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, 0])).unwrap().toBigInt();2152  }21532154  /**2155   * Set, change, or remove approved address to transfer tokens.2156   *2157   * @param signer keyring of signer2158   * @param collectionId ID of collection2159   * @param toAddressObj address to be approved2160   * @param amount amount of tokens to be approved2161   * @example approveTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n)2162   * @returns ```true``` if extrinsic success, otherwise ```false```2163   */2164  approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {2165    return super.approveToken(signer, collectionId, 0, toAddressObj, amount);2166  }21672168  /**2169   * Get amount of fungible tokens approved to transfer2170   * @param collectionId ID of collection2171   * @param fromAddressObj owner of tokens2172   * @param toAddressObj the address approved for the transfer of tokens on behalf of the owner2173   * @returns number of tokens approved for the transfer2174   */2175  getApprovedTokens(collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2176    return super.getTokenApprovedPieces(collectionId, 0, toAddressObj, fromAddressObj);2177  }2178}217921802181class ChainGroup extends HelperGroup<ChainHelperBase> {2182  /**2183   * Get system properties of a chain2184   * @example getChainProperties();2185   * @returns ss58Format, token decimals, and token symbol2186   */2187  getChainProperties(): IChainProperties {2188    const properties = (this.helper.getApi() as any).registry.getChainProperties().toJSON();2189    return {2190      ss58Format: properties.ss58Format.toJSON(),2191      tokenDecimals: properties.tokenDecimals.toJSON(),2192      tokenSymbol: properties.tokenSymbol.toJSON(),2193    };2194  }21952196  /**2197   * Get chain header2198   * @example getLatestBlockNumber();2199   * @returns the number of the last block2200   */2201  async getLatestBlockNumber(): Promise<number> {2202    return (await this.helper.callRpc('api.rpc.chain.getHeader')).number.toNumber();2203  }22042205  /**2206   * Get block hash by block number2207   * @param blockNumber number of block2208   * @example getBlockHashByNumber(12345);2209   * @returns hash of a block2210   */2211  async getBlockHashByNumber(blockNumber: number): Promise<string | null> {2212    const blockHash = (await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])).toJSON();2213    if(blockHash === '0x0000000000000000000000000000000000000000000000000000000000000000') return null;2214    return blockHash;2215  }22162217  // TODO add docs2218  async getBlock(blockHashOrNumber: string | number): Promise<IBlock | null> {2219    const blockHash = typeof blockHashOrNumber === 'string' ? blockHashOrNumber : await this.getBlockHashByNumber(blockHashOrNumber);2220    if (!blockHash) return null;2221    return (await this.helper.callRpc('api.rpc.chain.getBlock', [blockHash])).toHuman().block;2222  }22232224  /**2225   * Get latest relay block2226   * @returns {number} relay block2227   */2228  async getRelayBlockNumber(): Promise<bigint> {2229    const blockNumber = (await this.helper.callRpc('api.query.parachainSystem.validationData')).toJSON().relayParentNumber;2230    return BigInt(blockNumber);2231  }22322233  /**2234   * Get account nonce2235   * @param address substrate address2236   * @example getNonce("5GrwvaEF5zXb26Fz...");2237   * @returns number, account's nonce2238   */2239  async getNonce(address: TSubstrateAccount): Promise<number> {2240    return (await this.helper.callRpc('api.query.system.account', [address])).nonce.toNumber();2241  }2242}22432244class SubstrateBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2245  /**2246 * Get substrate address balance2247 * @param address substrate address2248 * @example getSubstrate("5GrwvaEF5zXb26Fz...")2249 * @returns amount of tokens on address2250 */2251  async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2252    return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt();2253  }22542255  /**2256   * Transfer tokens to substrate address2257   * @param signer keyring of signer2258   * @param address substrate address of a recipient2259   * @param amount amount of tokens to be transfered2260   * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n);2261   * @returns ```true``` if extrinsic success, otherwise ```false```2262   */2263  async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2264    const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true/*, `Unable to transfer balance from ${this.helper.getSignerAddress(signer)} to ${address}`*/);22652266    let transfer = {from: null, to: null, amount: 0n} as any;2267    result.result.events.forEach(({event: {data, method, section}}) => {2268      if ((section === 'balances') && (method === 'Transfer')) {2269        transfer = {2270          from: this.helper.address.normalizeSubstrate(data[0]),2271          to: this.helper.address.normalizeSubstrate(data[1]),2272          amount: BigInt(data[2]),2273        };2274      }2275    });2276    const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from2277      && this.helper.address.normalizeSubstrate(address) === transfer.to2278      && BigInt(amount) === transfer.amount;2279    return isSuccess;2280  }22812282  /**2283   * Get full substrate balance including free, miscFrozen, feeFrozen, and reserved2284   * @param address substrate address2285   * @returns2286   */2287  async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2288    const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data;2289    return {free: accountInfo.free.toBigInt(), miscFrozen: accountInfo.miscFrozen.toBigInt(), feeFrozen: accountInfo.feeFrozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};2290  }22912292  async getLocked(address: TSubstrateAccount): Promise<[{id: string, amount: bigint, reason: string}]> {2293    const locks = (await this.helper.callRpc('api.query.balances.locks', [address])).toHuman();2294    return locks.map((lock: any) => {return {id: lock.id, amount: BigInt(lock.amount.replace(/,/g, '')), reasons: lock.reasons};});2295  }2296}22972298class EthereumBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2299  /**2300   * Get ethereum address balance2301   * @param address ethereum address2302   * @example getEthereum("0x9F0583DbB855d...")2303   * @returns amount of tokens on address2304   */2305  async getEthereum(address: TEthereumAccount): Promise<bigint> {2306    return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt();2307  }23082309  /**2310   * Transfer tokens to address2311   * @param signer keyring of signer2312   * @param address Ethereum address of a recipient2313   * @param amount amount of tokens to be transfered2314   * @example transferToEthereum(alithKeyring, "0x9F0583DbB855d...", 100_000_000_000n);2315   * @returns ```true``` if extrinsic success, otherwise ```false```2316   */2317  async transferToEthereum(signer: TSigner, address: TEthereumAccount, amount: bigint | string): Promise<boolean> {2318    const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);23192320    let transfer = {from: null, to: null, amount: 0n} as any;2321    result.result.events.forEach(({event: {data, method, section}}) => {2322      if ((section === 'balances') && (method === 'Transfer')) {2323        transfer = {2324          from: data[0].toString(),2325          to: data[1].toString(),2326          amount: BigInt(data[2]),2327        };2328      }2329    });2330    const isSuccess = (typeof signer === 'string' ? signer : signer.address) === transfer.from2331      && address === transfer.to2332      && BigInt(amount) === transfer.amount;2333    return isSuccess;2334  }2335}23362337class BalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2338  subBalanceGroup: SubstrateBalanceGroup<T>;2339  ethBalanceGroup: EthereumBalanceGroup<T>;23402341  constructor(helper: T) {2342    super(helper);2343    this.subBalanceGroup = new SubstrateBalanceGroup(helper);2344    this.ethBalanceGroup = new EthereumBalanceGroup(helper);2345  }23462347  getCollectionCreationPrice(): bigint {2348    return 2n * this.getOneTokenNominal();2349  }2350  /**2351   * Representation of the native token in the smallest unit - one OPAL (OPL), QUARTZ (QTZ), or UNIQUE (UNQ).2352   * @example getOneTokenNominal()2353   * @returns ```BigInt``` representation of the native token in the smallest unit, e.g. ```1_000_000_000_000_000_000n``` for QTZ.2354   */2355  getOneTokenNominal(): bigint {2356    const chainProperties = this.helper.chain.getChainProperties();2357    return 10n ** BigInt((chainProperties.tokenDecimals || [18])[0]);2358  }23592360  /**2361   * Get substrate address balance2362   * @param address substrate address2363   * @example getSubstrate("5GrwvaEF5zXb26Fz...")2364   * @returns amount of tokens on address2365   */2366  getSubstrate(address: TSubstrateAccount): Promise<bigint> {2367    return this.subBalanceGroup.getSubstrate(address);2368  }23692370  /**2371   * Get full substrate balance including free, miscFrozen, feeFrozen, and reserved2372   * @param address substrate address2373   * @returns2374   */2375  getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2376    return this.subBalanceGroup.getSubstrateFull(address);2377  }23782379  /**2380   * Get locked balances2381   * @param address substrate address2382   * @returns locked balances with reason via api.query.balances.locks2383   */2384  getLocked(address: TSubstrateAccount) {2385    return this.subBalanceGroup.getLocked(address);2386  }23872388  /**2389   * Get ethereum address balance2390   * @param address ethereum address2391   * @example getEthereum("0x9F0583DbB855d...")2392   * @returns amount of tokens on address2393   */2394  getEthereum(address: TEthereumAccount): Promise<bigint> {2395    return this.ethBalanceGroup.getEthereum(address);2396  }23972398  /**2399   * Transfer tokens to substrate address2400   * @param signer keyring of signer2401   * @param address substrate address of a recipient2402   * @param amount amount of tokens to be transfered2403   * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n);2404   * @returns ```true``` if extrinsic success, otherwise ```false```2405   */2406  transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2407    return this.subBalanceGroup.transferToSubstrate(signer, address, amount);2408  }24092410  async forceTransferToSubstrate(signer: TSigner, from: TSubstrateAccount, to: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2411    const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceTransfer', [from, to, amount], true);24122413    let transfer = {from: null, to: null, amount: 0n} as any;2414    result.result.events.forEach(({event: {data, method, section}}) => {2415      if ((section === 'balances') && (method === 'Transfer')) {2416        transfer = {2417          from: this.helper.address.normalizeSubstrate(data[0]),2418          to: this.helper.address.normalizeSubstrate(data[1]),2419          amount: BigInt(data[2]),2420        };2421      }2422    });2423    let isSuccess = this.helper.address.normalizeSubstrate(from) === transfer.from;2424    isSuccess = isSuccess && this.helper.address.normalizeSubstrate(to) === transfer.to;2425    isSuccess = isSuccess && BigInt(amount) === transfer.amount;2426    return isSuccess;2427  }24282429  /**2430   * Transfer tokens with the unlock period2431   * @param signer signers Keyring2432   * @param address Substrate address of recipient2433   * @param schedule Schedule params2434   * @example vestedTransfer(signer, recepient.address, 20000, 100, 10, 50 * nominal); // total amount of vested tokens will be 100 * 50 = 50002435   */2436  async vestedTransfer(signer: TSigner, address: TSubstrateAccount, schedule: {start: bigint, period: bigint, periodCount: bigint, perPeriod: bigint}): Promise<void> {2437    const result = await this.helper.executeExtrinsic(signer, 'api.tx.vesting.vestedTransfer', [address, schedule]);2438    const event = result.result.events2439      .find(e => e.event.section === 'vesting' &&2440            e.event.method === 'VestingScheduleAdded' &&2441            e.event.data[0].toHuman() === signer.address);2442    if (!event) throw Error('Cannot find transfer in events');2443  }24442445  /**2446   * Get schedule for recepient of vested transfer2447   * @param address Substrate address of recipient2448   * @returns2449   */2450  async getVestingSchedules(address: TSubstrateAccount): Promise<{start: bigint, period: bigint, periodCount: bigint, perPeriod: bigint}[]> {2451    const schedule = (await this.helper.callRpc('api.query.vesting.vestingSchedules', [address])).toJSON();2452    return schedule.map((schedule: any) => {2453      return {2454        start: BigInt(schedule.start),2455        period: BigInt(schedule.period),2456        periodCount: BigInt(schedule.periodCount),2457        perPeriod: BigInt(schedule.perPeriod),2458      };2459    });2460  }24612462  /**2463   * Claim vested tokens2464   * @param signer signers Keyring2465   */2466  async claim(signer: TSigner) {2467    const result = await this.helper.executeExtrinsic(signer, 'api.tx.vesting.claim', []);2468    const event = result.result.events2469      .find(e => e.event.section === 'vesting' &&2470            e.event.method === 'Claimed' &&2471            e.event.data[0].toHuman() === signer.address);2472    if (!event) throw Error('Cannot find claim in events');2473  }2474}24752476class AddressGroup extends HelperGroup<ChainHelperBase> {2477  /**2478   * Normalizes the address to the specified ss58 format, by default ```42```.2479   * @param address substrate address2480   * @param ss58Format format for address conversion, by default ```42```2481   * @example normalizeSubstrate("unjKJQJrRd238pkUZZvzDQrfKuM39zBSnQ5zjAGAGcdRhaJTx") // returns 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY2482   * @returns substrate address converted to normalized (i.e., starting with 5) or specified explicitly representation2483   */2484  normalizeSubstrate(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {2485    return CrossAccountId.normalizeSubstrateAddress(address, ss58Format);2486  }24872488  /**2489   * Get address in the connected chain format2490   * @param address substrate address2491   * @example normalizeSubstrateToChainFormat("5GrwvaEF5zXb26Fz...") // returns unjKJQJrRd238pkUZZ... for Unique Network2492   * @returns address in chain format2493   */2494  normalizeSubstrateToChainFormat(address: TSubstrateAccount): TSubstrateAccount {2495    return this.normalizeSubstrate(address, this.helper.chain.getChainProperties().ss58Format);2496  }24972498  /**2499   * Get substrate mirror of an ethereum address2500   * @param ethAddress ethereum address2501   * @param toChainFormat false for normalized account2502   * @example ethToSubstrate('0x9F0583DbB855d...')2503   * @returns substrate mirror of a provided ethereum address2504   */2505  ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat=false): TSubstrateAccount {2506    return CrossAccountId.translateEthToSub(ethAddress, toChainFormat ? this.helper.chain.getChainProperties().ss58Format : undefined);2507  }25082509  /**2510   * Get ethereum mirror of a substrate address2511   * @param subAddress substrate account2512   * @example substrateToEth("5DnSF6RRjwteE3BrC...")2513   * @returns ethereum mirror of a provided substrate address2514   */2515  substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount {2516    return CrossAccountId.translateSubToEth(subAddress);2517  }25182519  /**2520   * Encode key to substrate address2521   * @param key key for encoding address2522   * @param ss58Format prefix for encoding to the address of the corresponding network2523   * @returns encoded substrate address2524   */2525  encodeSubstrateAddress (key: Uint8Array | string | bigint, ss58Format = 42): string {2526    const u8a :Uint8Array = typeof key === 'string'2527      ? hexToU8a(key)2528      : typeof key === 'bigint'2529        ? hexToU8a(key.toString(16))2530        : key;25312532    if (ss58Format < 0 || ss58Format > 16383 || [46, 47].includes(ss58Format)) {2533      throw new Error(`ss58Format is not valid, received ${typeof ss58Format} "${ss58Format}"`);2534    }25352536    const allowedDecodedLengths = [1, 2, 4, 8, 32, 33];2537    if (!allowedDecodedLengths.includes(u8a.length)) {2538      throw new Error(`key length is not valid, received ${u8a.length}, valid values are ${allowedDecodedLengths.join(', ')}`);2539    }25402541    const u8aPrefix = ss58Format < 642542      ? new Uint8Array([ss58Format])2543      : new Uint8Array([2544        ((ss58Format & 0xfc) >> 2) | 0x40,2545        (ss58Format >> 8) | ((ss58Format & 0x03) << 6),2546      ]);25472548    const input = u8aConcat(u8aPrefix, u8a);25492550    return base58Encode(u8aConcat(2551      input,2552      blake2AsU8a(input).subarray(0, [32, 33].includes(u8a.length) ? 2 : 1),2553    ));2554  }25552556  /**2557   * Restore substrate address from bigint representation2558   * @param number decimal representation of substrate address2559   * @returns substrate address2560   */2561  restoreCrossAccountFromBigInt(number: bigint): TSubstrateAccount {2562    if (this.helper.api === null) {2563      throw 'Not connected';2564    }2565    const res = this.helper.api.registry.createType('AccountId', '0x' + number.toString(16).padStart(64, '0')).toJSON();2566    if (res === undefined || res === null) {2567      throw 'Restore address error';2568    }2569    return res.toString();2570  }25712572  /**2573   * Convert etherium cross account id to substrate cross account id2574   * @param ethCrossAccount etherium cross account2575   * @returns substrate cross account id2576   */2577  convertCrossAccountFromEthCrossAccount(ethCrossAccount: IEthCrossAccountId): ICrossAccountId {2578    if (ethCrossAccount.sub === '0') {2579      return {Ethereum: ethCrossAccount.eth.toLocaleLowerCase()};2580    }25812582    const ss58 = this.restoreCrossAccountFromBigInt(BigInt(ethCrossAccount.sub));2583    return {Substrate: ss58};2584  }25852586  paraSiblingSovereignAccount(paraid: number) {2587    // We are getting a *sibling* parachain sovereign account,2588    // so we need a sibling prefix: encoded(b"sibl") == 0x7369626c2589    const siblingPrefix = '0x7369626c';25902591    const encodedParaId = this.helper.getApi().createType('u32', paraid).toHex(true).substring(2);2592    const suffix = '000000000000000000000000000000000000000000000000';25932594    return siblingPrefix + encodedParaId + suffix;2595  }2596}25972598class StakingGroup extends HelperGroup<UniqueHelper> {2599  /**2600   * Stake tokens for App Promotion2601   * @param signer keyring of signer2602   * @param amountToStake amount of tokens to stake2603   * @param label extra label for log2604   * @returns2605   */2606  async stake(signer: TSigner, amountToStake: bigint, label?: string): Promise<boolean> {2607    if(typeof label === 'undefined') label = `${signer.address} amount: ${amountToStake}`;2608    const _stakeResult = await this.helper.executeExtrinsic(2609      signer, 'api.tx.appPromotion.stake',2610      [amountToStake], true,2611    );2612    // TODO extract info from stakeResult2613    return true;2614  }26152616  /**2617   * Unstake tokens for App Promotion2618   * @param signer keyring of signer2619   * @param amountToUnstake amount of tokens to unstake2620   * @param label extra label for log2621   * @returns block number where balances will be unlocked2622   */2623  async unstake(signer: TSigner, label?: string): Promise<number> {2624    if(typeof label === 'undefined') label = `${signer.address}`;2625    const _unstakeResult = await this.helper.executeExtrinsic(2626      signer, 'api.tx.appPromotion.unstake',2627      [], true,2628    );2629    // TODO extract block number fron events2630    return 1;2631  }26322633  /**2634   * Get total staked amount for address2635   * @param address substrate or ethereum address2636   * @returns total staked amount2637   */2638  async getTotalStaked(address?: ICrossAccountId): Promise<bigint> {2639    if (address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt();2640    return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt();2641  }26422643  /**2644   * Get total staked per block2645   * @param address substrate or ethereum address2646   * @returns array of stakes. `block` – the number of the block in which the stake was made. `amount` - the number of tokens staked in the block2647   */2648  async getTotalStakedPerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2649    const rawTotalStakerdPerBlock = await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address]);2650    return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => {2651      return {2652        block: block.toBigInt(),2653        amount: amount.toBigInt(),2654      };2655    });2656  }26572658  /**2659   * Get total pending unstake amount for address2660   * @param address substrate or ethereum address2661   * @returns total pending unstake amount2662   */2663  async getPendingUnstake(address: ICrossAccountId): Promise<bigint> {2664    return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt();2665  }26662667  /**2668   * Get pending unstake amount per block for address2669   * @param address substrate or ethereum address2670   * @returns array of pending stakes. `block` – the number of the block in which the unstake was made. `amount` - the number of tokens unstaked in the block2671   */2672  async getPendingUnstakePerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2673    const rawUnstakedPerBlock = await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address]);2674    const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => {2675      return {2676        block: block.toBigInt(),2677        amount: amount.toBigInt(),2678      };2679    });2680    return result;2681  }2682}26832684class SchedulerGroup extends HelperGroup<UniqueHelper> {2685  constructor(helper: UniqueHelper) {2686    super(helper);2687  }26882689  cancelScheduled(signer: TSigner, scheduledId: string) {2690    return this.helper.executeExtrinsic(2691      signer,2692      'api.tx.scheduler.cancelNamed',2693      [scheduledId],2694      true,2695    );2696  }26972698  changePriority(signer: TSigner, scheduledId: string, priority: number) {2699    return this.helper.executeExtrinsic(2700      signer,2701      'api.tx.scheduler.changeNamedPriority',2702      [scheduledId, priority],2703      true,2704    );2705  }27062707  scheduleAt<T extends UniqueHelper>(2708    executionBlockNumber: number,2709    options: ISchedulerOptions = {},2710  ) {2711    return this.schedule<T>('schedule', executionBlockNumber, options);2712  }27132714  scheduleAfter<T extends UniqueHelper>(2715    blocksBeforeExecution: number,2716    options: ISchedulerOptions = {},2717  ) {2718    return this.schedule<T>('scheduleAfter', blocksBeforeExecution, options);2719  }27202721  schedule<T extends UniqueHelper>(2722    scheduleFn: 'schedule' | 'scheduleAfter',2723    blocksNum: number,2724    options: ISchedulerOptions = {},2725  ) {2726    // eslint-disable-next-line @typescript-eslint/naming-convention2727    const ScheduledHelperType = ScheduledUniqueHelper(this.helper.helperBase);2728    return this.helper.clone(ScheduledHelperType, {2729      scheduleFn,2730      blocksNum,2731      options,2732    }) as T;2733  }2734}27352736class CollatorSelectionGroup extends HelperGroup<UniqueHelper> {2737  //todo:collator documentation2738  addInvulnerable(signer: TSigner, address: string) {2739    return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.addInvulnerable', [address]);2740  }27412742  removeInvulnerable(signer: TSigner, address: string) {2743    return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.removeInvulnerable', [address]);2744  }27452746  async getInvulnerables(): Promise<string[]> {2747    return (await this.helper.callRpc('api.query.collatorSelection.invulnerables')).map((x: any) => x.toHuman());2748  }27492750  /** and also total max invulnerables */2751  maxCollators(): number {2752    return (this.helper.getApi().consts.configuration.defaultCollatorSelectionMaxCollators.toJSON() as number);2753  }27542755  async getDesiredCollators(): Promise<number> {2756    return (await this.helper.callRpc('api.query.configuration.collatorSelectionDesiredCollatorsOverride')).toNumber();2757  }27582759  setLicenseBond(signer: TSigner, amount: bigint) {2760    return this.helper.executeExtrinsic(signer, 'api.tx.configuration.setCollatorSelectionLicenseBond', [amount]);2761  }27622763  async getLicenseBond(): Promise<bigint> {2764    return (await this.helper.callRpc('api.query.configuration.collatorSelectionLicenseBondOverride')).toBigInt();2765  }27662767  obtainLicense(signer: TSigner) {2768    return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.getLicense', []);2769  }27702771  releaseLicense(signer: TSigner) {2772    return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.releaseLicense', []);2773  }27742775  forceReleaseLicense(signer: TSigner, released: string) {2776    return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.forceReleaseLicense', [released]);2777  }27782779  async hasLicense(address: string): Promise<bigint> {2780    return (await this.helper.callRpc('api.query.collatorSelection.licenseDepositOf', [address])).toBigInt();2781  }27822783  onboard(signer: TSigner) {2784    return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.onboard', []);2785  }27862787  offboard(signer: TSigner) {2788    return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.offboard', []);2789  }27902791  async getCandidates(): Promise<string[]> {2792    return (await this.helper.callRpc('api.query.collatorSelection.candidates')).map((x: any) => x.toHuman());2793  }2794}27952796class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {2797  async register(signer: TSigner, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {2798    await this.helper.executeExtrinsic(2799      signer,2800      'api.tx.foreignAssets.registerForeignAsset',2801      [ownerAddress, location, metadata],2802      true,2803    );2804  }28052806  async update(signer: TSigner, foreignAssetId: number, location: any, metadata: IForeignAssetMetadata) {2807    await this.helper.executeExtrinsic(2808      signer,2809      'api.tx.foreignAssets.updateForeignAsset',2810      [foreignAssetId, location, metadata],2811      true,2812    );2813  }2814}28152816class XcmGroup<T extends ChainHelperBase> extends HelperGroup<T> {2817  palletName: string;28182819  constructor(helper: T, palletName: string) {2820    super(helper);28212822    this.palletName = palletName;2823  }28242825  async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: any) {2826    await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, weightLimit], true);2827  }28282829  async teleportAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number) {2830    await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.teleportAssets`, [destination, beneficiary, assets, feeAssetItem], true);2831  }28322833  async teleportNativeAsset(signer: TSigner, destinationParaId: number, targetAccount: Uint8Array, amount: bigint) {2834    const destination = {2835      V1: {2836        parents: 0,2837        interior: {2838          X1: {2839            Parachain: destinationParaId,2840          },2841        },2842      },2843    };28442845    const beneficiary = {2846      V1: {2847        parents: 0,2848        interior: {2849          X1: {2850            AccountId32: {2851              network: 'Any',2852              id: targetAccount,2853            },2854          },2855        },2856      },2857    };28582859    const assets = {2860      V1: [2861        {2862          id: {2863            Concrete: {2864              parents: 0,2865              interior: 'Here',2866            },2867          },2868          fun: {2869            Fungible: amount,2870          },2871        },2872      ],2873    };28742875    const feeAssetItem = 0;28762877    await this.teleportAssets(signer, destination, beneficiary, assets, feeAssetItem);2878  }2879}28802881class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2882  async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: any) {2883    await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);2884  }28852886  async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: any) {2887    await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);2888  }28892890  async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: any) {2891    await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);2892  }2893}28942895class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2896  async accounts(address: string, currencyId: any) {2897    const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;2898    return BigInt(free);2899  }2900}29012902class AssetsGroup<T extends ChainHelperBase> extends HelperGroup<T> {2903  async create(signer: TSigner, assetId: number, admin: string, minimalBalance: bigint) {2904    await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true);2905  }29062907  async setMetadata(signer: TSigner, assetId: number, name: string, symbol: string, decimals: number) {2908    await this.helper.executeExtrinsic(signer, 'api.tx.assets.setMetadata', [assetId, name, symbol, decimals], true);2909  }29102911  async mint(signer: TSigner, assetId: number, beneficiary: string, amount: bigint) {2912    await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);2913  }29142915  async account(assetId: string | number, address: string) {2916    const accountAsset = (2917      await this.helper.callRpc('api.query.assets.account', [assetId, address])2918    ).toJSON()! as any;29192920    if (accountAsset !== null) {2921      return BigInt(accountAsset['balance']);2922    } else {2923      return null;2924    }2925  }2926}29272928class AcalaAssetRegistryGroup extends HelperGroup<AcalaHelper> {2929  async registerForeignAsset(signer: TSigner, destination: any, metadata: AcalaAssetMetadata) {2930    await this.helper.executeExtrinsic(signer, 'api.tx.assetRegistry.registerForeignAsset', [destination, metadata], true);2931  }2932}29332934class MoonbeamAssetManagerGroup extends HelperGroup<MoonbeamHelper> {2935  makeRegisterForeignAssetProposal(assetInfo: MoonbeamAssetInfo) {2936    const apiPrefix = 'api.tx.assetManager.';29372938    const registerTx = this.helper.constructApiCall(2939      apiPrefix + 'registerForeignAsset',2940      [assetInfo.location, assetInfo.metadata, assetInfo.existentialDeposit, assetInfo.isSufficient],2941    );29422943    const setUnitsTx = this.helper.constructApiCall(2944      apiPrefix + 'setAssetUnitsPerSecond',2945      [assetInfo.location, assetInfo.unitsPerSecond, assetInfo.numAssetsWeightHint],2946    );29472948    const batchCall = this.helper.getApi().tx.utility.batchAll([registerTx, setUnitsTx]);2949    const encodedProposal = batchCall?.method.toHex() || '';2950    return encodedProposal;2951  }29522953  async assetTypeId(location: any) {2954    return await this.helper.callRpc('api.query.assetManager.assetTypeId', [location]);2955  }2956}29572958class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {2959  notePreimagePallet: string;29602961  constructor(helper: MoonbeamHelper, options: {[key: string]: any} = {}) {2962    super(helper);2963    this.notePreimagePallet = options.notePreimagePallet;2964  }29652966  async notePreimage(signer: TSigner, encodedProposal: string) {2967    await this.helper.executeExtrinsic(signer, `api.tx.${this.notePreimagePallet}.notePreimage`, [encodedProposal], true);2968  }29692970  externalProposeMajority(proposal: any) {2971    return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposal]);2972  }29732974  fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {2975    return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);2976  }29772978  async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {2979    await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);2980  }2981}29822983class MoonbeamCollectiveGroup extends HelperGroup<MoonbeamHelper> {2984  collective: string;29852986  constructor(helper: MoonbeamHelper, collective: string) {2987    super(helper);29882989    this.collective = collective;2990  }29912992  async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {2993    await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);2994  }29952996  async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {2997    await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);2998  }29993000  async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: any, lengthBound: number) {3001    await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);3002  }30033004  async proposalCount() {3005    return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));3006  }3007}30083009export type ChainHelperBaseConstructor = new(...args: any[]) => ChainHelperBase;3010export type UniqueHelperConstructor = new(...args: any[]) => UniqueHelper;30113012export class UniqueHelper extends ChainHelperBase {3013  balance: BalanceGroup<UniqueHelper>;3014  collection: CollectionGroup;3015  nft: NFTGroup;3016  rft: RFTGroup;3017  ft: FTGroup;3018  staking: StakingGroup;3019  scheduler: SchedulerGroup;3020  collatorSelection: CollatorSelectionGroup;3021  foreignAssets: ForeignAssetsGroup;3022  xcm: XcmGroup<UniqueHelper>;3023  xTokens: XTokensGroup<UniqueHelper>;3024  tokens: TokensGroup<UniqueHelper>;30253026  constructor(logger?: ILogger, options: {[key: string]: any} = {}) {3027    super(logger, options.helperBase ?? UniqueHelper);30283029    this.balance = new BalanceGroup(this);3030    this.collection = new CollectionGroup(this);3031    this.nft = new NFTGroup(this);3032    this.rft = new RFTGroup(this);3033    this.ft = new FTGroup(this);3034    this.staking = new StakingGroup(this);3035    this.scheduler = new SchedulerGroup(this);3036    this.collatorSelection = new CollatorSelectionGroup(this);3037    this.foreignAssets = new ForeignAssetsGroup(this);3038    this.xcm = new XcmGroup(this, 'polkadotXcm');3039    this.xTokens = new XTokensGroup(this);3040    this.tokens = new TokensGroup(this);3041  }30423043  getSudo<T extends UniqueHelper>() {3044    // eslint-disable-next-line @typescript-eslint/naming-convention3045    const SudoHelperType = SudoHelper(this.helperBase);3046    return this.clone(SudoHelperType) as T;3047  }3048}30493050export class XcmChainHelper extends ChainHelperBase {3051  async connect(wsEndpoint: string, _listeners?: any): Promise<void> {3052    const wsProvider = new WsProvider(wsEndpoint);3053    this.api = new ApiPromise({3054      provider: wsProvider,3055    });3056    await this.api.isReadyOrError;3057    this.network = await UniqueHelper.detectNetwork(this.api);3058  }3059}30603061export class RelayHelper extends XcmChainHelper {3062  balance: SubstrateBalanceGroup<RelayHelper>;3063  xcm: XcmGroup<RelayHelper>;30643065  constructor(logger?: ILogger, options: {[key: string]: any} = {}) {3066    super(logger, options.helperBase ?? RelayHelper);30673068    this.balance = new SubstrateBalanceGroup(this);3069    this.xcm = new XcmGroup(this, 'xcmPallet');3070  }3071}30723073export class WestmintHelper extends XcmChainHelper {3074  balance: SubstrateBalanceGroup<WestmintHelper>;3075  xcm: XcmGroup<WestmintHelper>;3076  assets: AssetsGroup<WestmintHelper>;3077  xTokens: XTokensGroup<WestmintHelper>;30783079  constructor(logger?: ILogger, options: {[key: string]: any} = {}) {3080    super(logger, options.helperBase ?? WestmintHelper);30813082    this.balance = new SubstrateBalanceGroup(this);3083    this.xcm = new XcmGroup(this, 'polkadotXcm');3084    this.assets = new AssetsGroup(this);3085    this.xTokens = new XTokensGroup(this);3086  }3087}30883089export class MoonbeamHelper extends XcmChainHelper {3090  balance: EthereumBalanceGroup<MoonbeamHelper>;3091  assetManager: MoonbeamAssetManagerGroup;3092  assets: AssetsGroup<MoonbeamHelper>;3093  xTokens: XTokensGroup<MoonbeamHelper>;3094  democracy: MoonbeamDemocracyGroup;3095  collective: {3096    council: MoonbeamCollectiveGroup,3097    techCommittee: MoonbeamCollectiveGroup,3098  };30993100  constructor(logger?: ILogger, options: {[key: string]: any} = {}) {3101    super(logger, options.helperBase ?? MoonbeamHelper);31023103    this.balance = new EthereumBalanceGroup(this);3104    this.assetManager = new MoonbeamAssetManagerGroup(this);3105    this.assets = new AssetsGroup(this);3106    this.xTokens = new XTokensGroup(this);3107    this.democracy = new MoonbeamDemocracyGroup(this, options);3108    this.collective = {3109      council: new MoonbeamCollectiveGroup(this, 'councilCollective'),3110      techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),3111    };3112  }3113}31143115export class AcalaHelper extends XcmChainHelper {3116  balance: SubstrateBalanceGroup<AcalaHelper>;3117  assetRegistry: AcalaAssetRegistryGroup;3118  xTokens: XTokensGroup<AcalaHelper>;3119  tokens: TokensGroup<AcalaHelper>;31203121  constructor(logger?: ILogger, options: {[key: string]: any} = {}) {3122    super(logger, options.helperBase ?? AcalaHelper);31233124    this.balance = new SubstrateBalanceGroup(this);3125    this.assetRegistry = new AcalaAssetRegistryGroup(this);3126    this.xTokens = new XTokensGroup(this);3127    this.tokens = new TokensGroup(this);3128  }31293130  getSudo<T extends AcalaHelper>() {3131    // eslint-disable-next-line @typescript-eslint/naming-convention3132    const SudoHelperType = SudoHelper(this.helperBase);3133    return this.clone(SudoHelperType) as T;3134  }3135}31363137// eslint-disable-next-line @typescript-eslint/naming-convention3138function ScheduledUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {3139  return class extends Base {3140    scheduleFn: 'schedule' | 'scheduleAfter';3141    blocksNum: number;3142    options: ISchedulerOptions;31433144    constructor(...args: any[]) {3145      const logger = args[0] as ILogger;3146      const options = args[1] as {3147        scheduleFn: 'schedule' | 'scheduleAfter',3148        blocksNum: number,3149        options: ISchedulerOptions3150      };31513152      super(logger);31533154      this.scheduleFn = options.scheduleFn;3155      this.blocksNum = options.blocksNum;3156      this.options = options.options;3157    }31583159    executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise<ITransactionResult> {3160      const scheduledTx = this.constructApiCall(scheduledExtrinsic, scheduledParams);31613162      const mandatorySchedArgs = [3163        this.blocksNum,3164        this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null,3165        this.options.priority ?? null,3166        scheduledTx,3167      ];31683169      let schedArgs;3170      let scheduleFn;31713172      if (this.options.scheduledId) {3173        schedArgs = [this.options.scheduledId!, ...mandatorySchedArgs];31743175        if (this.scheduleFn == 'schedule') {3176          scheduleFn = 'scheduleNamed';3177        } else if (this.scheduleFn == 'scheduleAfter') {3178          scheduleFn = 'scheduleNamedAfter';3179        }3180      } else {3181        schedArgs = mandatorySchedArgs;3182        scheduleFn = this.scheduleFn;3183      }31843185      const extrinsic = 'api.tx.scheduler.' +  scheduleFn;31863187      return super.executeExtrinsic(3188        sender,3189        extrinsic,3190        schedArgs,3191        expectSuccess,3192      );3193    }3194  };3195}31963197// eslint-disable-next-line @typescript-eslint/naming-convention3198function SudoHelper<T extends ChainHelperBaseConstructor>(Base: T) {3199  return class extends Base {3200    constructor(...args: any[]) {3201      super(...args);3202    }32033204    async executeExtrinsic(3205      sender: IKeyringPair,3206      extrinsic: string,3207      params: any[],3208      expectSuccess?: boolean,3209      options: Partial<SignerOptions>|null = null,3210    ): Promise<ITransactionResult> {3211      const call = this.constructApiCall(extrinsic, params);3212      const result = await super.executeExtrinsic(3213        sender,3214        'api.tx.sudo.sudo',3215        [call],3216        expectSuccess,3217        options,3218      );32193220      if (result.status === 'Fail') return result;32213222      const data = (result.result.events.find(x => x.event.section == 'sudo' && x.event.method == 'Sudid')?.event.data as any).sudoResult;3223      if (data.isErr) {3224        if (data.asErr.isModule) {3225          const error = (result.result.events[1].event.data as any).sudoResult.asErr.asModule;3226          const metaError = super.getApi()?.registry.findMetaError(error);3227          throw new Error(`${metaError.section}.${metaError.name}`);3228        } else {3229          throw new Error(data.asErr.toHuman());3230        }3231      }3232      return result;3233    }3234  };3235}32363237export class UniqueBaseCollection {3238  helper: UniqueHelper;3239  collectionId: number;32403241  constructor(collectionId: number, uniqueHelper: UniqueHelper) {3242    this.collectionId = collectionId;3243    this.helper = uniqueHelper;3244  }32453246  async getData() {3247    return await this.helper.collection.getData(this.collectionId);3248  }32493250  async getLastTokenId() {3251    return await this.helper.collection.getLastTokenId(this.collectionId);3252  }32533254  async doesTokenExist(tokenId: number) {3255    return await this.helper.collection.doesTokenExist(this.collectionId, tokenId);3256  }32573258  async getAdmins() {3259    return await this.helper.collection.getAdmins(this.collectionId);3260  }32613262  async getAllowList() {3263    return await this.helper.collection.getAllowList(this.collectionId);3264  }32653266  async getEffectiveLimits() {3267    return await this.helper.collection.getEffectiveLimits(this.collectionId);3268  }32693270  async getProperties(propertyKeys?: string[] | null) {3271    return await this.helper.collection.getProperties(this.collectionId, propertyKeys);3272  }32733274  async getPropertiesConsumedSpace() {3275    return await this.helper.collection.getPropertiesConsumedSpace(this.collectionId);3276  }32773278  async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) {3279    return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);3280  }32813282  async getOptions() {3283    return await this.helper.collection.getCollectionOptions(this.collectionId);3284  }32853286  async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) {3287    return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress);3288  }32893290  async confirmSponsorship(signer: TSigner) {3291    return await this.helper.collection.confirmSponsorship(signer, this.collectionId);3292  }32933294  async removeSponsor(signer: TSigner) {3295    return await this.helper.collection.removeSponsor(signer, this.collectionId);3296  }32973298  async setLimits(signer: TSigner, limits: ICollectionLimits) {3299    return await this.helper.collection.setLimits(signer, this.collectionId, limits);3300  }33013302  async changeOwner(signer: TSigner, ownerAddress: TSubstrateAccount) {3303    return await this.helper.collection.changeOwner(signer, this.collectionId, ownerAddress);3304  }33053306  async addAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {3307    return await this.helper.collection.addAdmin(signer, this.collectionId, adminAddressObj);3308  }33093310  async addToAllowList(signer: TSigner, addressObj: ICrossAccountId) {3311    return await this.helper.collection.addToAllowList(signer, this.collectionId, addressObj);3312  }33133314  async removeFromAllowList(signer: TSigner, addressObj: ICrossAccountId) {3315    return await this.helper.collection.removeFromAllowList(signer, this.collectionId, addressObj);3316  }33173318  async removeAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {3319    return await this.helper.collection.removeAdmin(signer, this.collectionId, adminAddressObj);3320  }33213322  async setProperties(signer: TSigner, properties: IProperty[]) {3323    return await this.helper.collection.setProperties(signer, this.collectionId, properties);3324  }33253326  async deleteProperties(signer: TSigner, propertyKeys: string[]) {3327    return await this.helper.collection.deleteProperties(signer, this.collectionId, propertyKeys);3328  }33293330  async setPermissions(signer: TSigner, permissions: ICollectionPermissions) {3331    return await this.helper.collection.setPermissions(signer, this.collectionId, permissions);3332  }33333334  async enableNesting(signer: TSigner, permissions: INestingPermissions) {3335    return await this.helper.collection.enableNesting(signer, this.collectionId, permissions);3336  }33373338  async disableNesting(signer: TSigner) {3339    return await this.helper.collection.disableNesting(signer, this.collectionId);3340  }33413342  async burn(signer: TSigner) {3343    return await this.helper.collection.burn(signer, this.collectionId);3344  }33453346  scheduleAt<T extends UniqueHelper>(3347    executionBlockNumber: number,3348    options: ISchedulerOptions = {},3349  ) {3350    const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3351    return new UniqueBaseCollection(this.collectionId, scheduledHelper);3352  }33533354  scheduleAfter<T extends UniqueHelper>(3355    blocksBeforeExecution: number,3356    options: ISchedulerOptions = {},3357  ) {3358    const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3359    return new UniqueBaseCollection(this.collectionId, scheduledHelper);3360  }33613362  getSudo<T extends UniqueHelper>() {3363    return new UniqueBaseCollection(this.collectionId, this.helper.getSudo<T>());3364  }3365}336633673368export class UniqueNFTCollection extends UniqueBaseCollection {3369  getTokenObject(tokenId: number) {3370    return new UniqueNFToken(tokenId, this);3371  }33723373  async getTokensByAddress(addressObj: ICrossAccountId) {3374    return await this.helper.nft.getTokensByAddress(this.collectionId, addressObj);3375  }33763377  async getToken(tokenId: number, blockHashAt?: string) {3378    return await this.helper.nft.getToken(this.collectionId, tokenId, [], blockHashAt);3379  }33803381  async getTokenOwner(tokenId: number, blockHashAt?: string) {3382    return await this.helper.nft.getTokenOwner(this.collectionId, tokenId, blockHashAt);3383  }33843385  async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {3386    return await this.helper.nft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);3387  }33883389  async getTokenChildren(tokenId: number, blockHashAt?: string) {3390    return await this.helper.nft.getTokenChildren(this.collectionId, tokenId, blockHashAt);3391  }33923393  async getPropertyPermissions(propertyKeys: string[] | null = null) {3394    return await this.helper.nft.getPropertyPermissions(this.collectionId, propertyKeys);3395  }33963397  async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3398    return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3399  }34003401  async getTokenPropertiesConsumedSpace(tokenId: number): Promise<number> {3402    const api = this.helper.getApi();3403    const props = (await api.query.nonfungible.tokenProperties(this.collectionId, tokenId)).toJSON();34043405    return (props! as any).consumedSpace;3406  }34073408  async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) {3409    return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj);3410  }34113412  async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3413    return await this.helper.nft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj);3414  }34153416  async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId) {3417    return await this.helper.nft.approveToken(signer, this.collectionId, tokenId, toAddressObj);3418  }34193420  async isTokenApproved(tokenId: number, toAddressObj: ICrossAccountId) {3421    return await this.helper.nft.isTokenApproved(this.collectionId, tokenId, toAddressObj);3422  }34233424  async mintToken(signer: TSigner, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3425    return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties});3426  }34273428  async mintMultipleTokens(signer: TSigner, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]) {3429    return await this.helper.nft.mintMultipleTokens(signer, this.collectionId, tokens);3430  }34313432  async burnToken(signer: TSigner, tokenId: number) {3433    return await this.helper.nft.burnToken(signer, this.collectionId, tokenId);3434  }34353436  async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId) {3437    return await this.helper.nft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj);3438  }34393440  async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3441    return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties);3442  }34433444  async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3445    return await this.helper.nft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3446  }34473448  async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3449    return await this.helper.nft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3450  }34513452  async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) {3453    return await this.helper.nft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj);3454  }34553456  async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3457    return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);3458  }34593460  scheduleAt<T extends UniqueHelper>(3461    executionBlockNumber: number,3462    options: ISchedulerOptions = {},3463  ) {3464    const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3465    return new UniqueNFTCollection(this.collectionId, scheduledHelper);3466  }34673468  scheduleAfter<T extends UniqueHelper>(3469    blocksBeforeExecution: number,3470    options: ISchedulerOptions = {},3471  ) {3472    const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3473    return new UniqueNFTCollection(this.collectionId, scheduledHelper);3474  }34753476  getSudo<T extends UniqueHelper>() {3477    return new UniqueNFTCollection(this.collectionId, this.helper.getSudo<T>());3478  }3479}348034813482export class UniqueRFTCollection extends UniqueBaseCollection {3483  getTokenObject(tokenId: number) {3484    return new UniqueRFToken(tokenId, this);3485  }34863487  async getToken(tokenId: number, blockHashAt?: string) {3488    return await this.helper.rft.getToken(this.collectionId, tokenId, [], blockHashAt);3489  }34903491  async getTokensByAddress(addressObj: ICrossAccountId) {3492    return await this.helper.rft.getTokensByAddress(this.collectionId, addressObj);3493  }34943495  async getTop10TokenOwners(tokenId: number) {3496    return await this.helper.rft.getTokenTop10Owners(this.collectionId, tokenId);3497  }34983499  async getTokenBalance(tokenId: number, addressObj: ICrossAccountId) {3500    return await this.helper.rft.getTokenBalance(this.collectionId, tokenId, addressObj);3501  }35023503  async getTokenTotalPieces(tokenId: number) {3504    return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId);3505  }35063507  async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3508    return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj);3509  }35103511  async getPropertyPermissions(propertyKeys: string[] | null = null) {3512    return await this.helper.rft.getPropertyPermissions(this.collectionId, propertyKeys);3513  }35143515  async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3516    return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3517  }35183519  async getTokenPropertiesConsumedSpace(tokenId: number): Promise<number> {3520    const api = this.helper.getApi();3521    const props = (await api.query.refungible.tokenProperties(this.collectionId, tokenId)).toJSON();35223523    return (props! as any).consumedSpace;3524  }35253526  async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount=1n) {3527    return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount);3528  }35293530  async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3531    return await this.helper.rft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj, amount);3532  }35333534  async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {3535    return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, amount);3536  }35373538  async repartitionToken(signer: TSigner, tokenId: number, amount: bigint) {3539    return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount);3540  }35413542  async mintToken(signer: TSigner, pieces=1n, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3543    return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties});3544  }35453546  async mintMultipleTokens(signer: TSigner, tokens: {pieces: bigint, owner: ICrossAccountId, properties?: IProperty[]}[]) {3547    return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens);3548  }35493550  async burnToken(signer: TSigner, tokenId: number, amount=1n) {3551    return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, amount);3552  }35533554  async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId,  amount=1n) {3555    return await this.helper.rft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, amount);3556  }35573558  async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3559    return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties);3560  }35613562  async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3563    return await this.helper.rft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3564  }35653566  async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3567    return await this.helper.rft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3568  }35693570  scheduleAt<T extends UniqueHelper>(3571    executionBlockNumber: number,3572    options: ISchedulerOptions = {},3573  ) {3574    const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3575    return new UniqueRFTCollection(this.collectionId, scheduledHelper);3576  }35773578  scheduleAfter<T extends UniqueHelper>(3579    blocksBeforeExecution: number,3580    options: ISchedulerOptions = {},3581  ) {3582    const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3583    return new UniqueRFTCollection(this.collectionId, scheduledHelper);3584  }35853586  getSudo<T extends UniqueHelper>() {3587    return new UniqueRFTCollection(this.collectionId, this.helper.getSudo<T>());3588  }3589}359035913592export class UniqueFTCollection extends UniqueBaseCollection {3593  async getBalance(addressObj: ICrossAccountId) {3594    return await this.helper.ft.getBalance(this.collectionId, addressObj);3595  }35963597  async getTotalPieces() {3598    return await this.helper.ft.getTotalPieces(this.collectionId);3599  }36003601  async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3602    return await this.helper.ft.getApprovedTokens(this.collectionId, fromAddressObj, toAddressObj);3603  }36043605  async getTop10Owners() {3606    return await this.helper.ft.getTop10Owners(this.collectionId);3607  }36083609  async mint(signer: TSigner, amount=1n, owner: ICrossAccountId = {Substrate: signer.address}) {3610    return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner);3611  }36123613  async mintWithOneOwner(signer: TSigner, tokens: {value: bigint}[], owner: ICrossAccountId = {Substrate: signer.address}) {3614    return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, tokens, owner);3615  }36163617  async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3618    return await this.helper.ft.transfer(signer, this.collectionId, toAddressObj, amount);3619  }36203621  async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3622    return await this.helper.ft.transferFrom(signer, this.collectionId, fromAddressObj, toAddressObj, amount);3623  }36243625  async burnTokens(signer: TSigner, amount=1n) {3626    return await this.helper.ft.burnTokens(signer, this.collectionId, amount);3627  }36283629  async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {3630    return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount);3631  }36323633  async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3634    return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount);3635  }36363637  scheduleAt<T extends UniqueHelper>(3638    executionBlockNumber: number,3639    options: ISchedulerOptions = {},3640  ) {3641    const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3642    return new UniqueFTCollection(this.collectionId, scheduledHelper);3643  }36443645  scheduleAfter<T extends UniqueHelper>(3646    blocksBeforeExecution: number,3647    options: ISchedulerOptions = {},3648  ) {3649    const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3650    return new UniqueFTCollection(this.collectionId, scheduledHelper);3651  }36523653  getSudo<T extends UniqueHelper>() {3654    return new UniqueFTCollection(this.collectionId, this.helper.getSudo<T>());3655  }3656}365736583659export class UniqueBaseToken {3660  collection: UniqueNFTCollection | UniqueRFTCollection;3661  collectionId: number;3662  tokenId: number;36633664  constructor(tokenId: number, collection: UniqueNFTCollection | UniqueRFTCollection) {3665    this.collection = collection;3666    this.collectionId = collection.collectionId;3667    this.tokenId = tokenId;3668  }36693670  async getNextSponsored(addressObj: ICrossAccountId) {3671    return await this.collection.getTokenNextSponsored(this.tokenId, addressObj);3672  }36733674  async getProperties(propertyKeys?: string[] | null) {3675    return await this.collection.getTokenProperties(this.tokenId, propertyKeys);3676  }36773678  async getTokenPropertiesConsumedSpace() {3679    return await this.collection.getTokenPropertiesConsumedSpace(this.tokenId);3680  }36813682  async setProperties(signer: TSigner, properties: IProperty[]) {3683    return await this.collection.setTokenProperties(signer, this.tokenId, properties);3684  }36853686  async deleteProperties(signer: TSigner, propertyKeys: string[]) {3687    return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys);3688  }36893690  async doesExist() {3691    return await this.collection.doesTokenExist(this.tokenId);3692  }36933694  nestingAccount() {3695    return this.collection.helper.util.getTokenAccount(this);3696  }36973698  scheduleAt<T extends UniqueHelper>(3699    executionBlockNumber: number,3700    options: ISchedulerOptions = {},3701  ) {3702    const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);3703    return new UniqueBaseToken(this.tokenId, scheduledCollection);3704  }37053706  scheduleAfter<T extends UniqueHelper>(3707    blocksBeforeExecution: number,3708    options: ISchedulerOptions = {},3709  ) {3710    const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);3711    return new UniqueBaseToken(this.tokenId, scheduledCollection);3712  }37133714  getSudo<T extends UniqueHelper>() {3715    return new UniqueBaseToken(this.tokenId, this.collection.getSudo<T>());3716  }3717}371837193720export class UniqueNFToken extends UniqueBaseToken {3721  collection: UniqueNFTCollection;37223723  constructor(tokenId: number, collection: UniqueNFTCollection) {3724    super(tokenId, collection);3725    this.collection = collection;3726  }37273728  async getData(blockHashAt?: string) {3729    return await this.collection.getToken(this.tokenId, blockHashAt);3730  }37313732  async getOwner(blockHashAt?: string) {3733    return await this.collection.getTokenOwner(this.tokenId, blockHashAt);3734  }37353736  async getTopmostOwner(blockHashAt?: string) {3737    return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);3738  }37393740  async getChildren(blockHashAt?: string) {3741    return await this.collection.getTokenChildren(this.tokenId, blockHashAt);3742  }37433744  async nest(signer: TSigner, toTokenObj: IToken) {3745    return await this.collection.nestToken(signer, this.tokenId, toTokenObj);3746  }37473748  async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3749    return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj);3750  }37513752  async transfer(signer: TSigner, addressObj: ICrossAccountId) {3753    return await this.collection.transferToken(signer, this.tokenId, addressObj);3754  }37553756  async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3757    return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj);3758  }37593760  async approve(signer: TSigner, toAddressObj: ICrossAccountId) {3761    return await this.collection.approveToken(signer, this.tokenId, toAddressObj);3762  }37633764  async isApproved(toAddressObj: ICrossAccountId) {3765    return await this.collection.isTokenApproved(this.tokenId, toAddressObj);3766  }37673768  async burn(signer: TSigner) {3769    return await this.collection.burnToken(signer, this.tokenId);3770  }37713772  async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId) {3773    return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj);3774  }37753776  scheduleAt<T extends UniqueHelper>(3777    executionBlockNumber: number,3778    options: ISchedulerOptions = {},3779  ) {3780    const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);3781    return new UniqueNFToken(this.tokenId, scheduledCollection);3782  }37833784  scheduleAfter<T extends UniqueHelper>(3785    blocksBeforeExecution: number,3786    options: ISchedulerOptions = {},3787  ) {3788    const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);3789    return new UniqueNFToken(this.tokenId, scheduledCollection);3790  }37913792  getSudo<T extends UniqueHelper>() {3793    return new UniqueNFToken(this.tokenId, this.collection.getSudo<T>());3794  }3795}37963797export class UniqueRFToken extends UniqueBaseToken {3798  collection: UniqueRFTCollection;37993800  constructor(tokenId: number, collection: UniqueRFTCollection) {3801    super(tokenId, collection);3802    this.collection = collection;3803  }38043805  async getData(blockHashAt?: string) {3806    return await this.collection.getToken(this.tokenId, blockHashAt);3807  }38083809  async getTop10Owners() {3810    return await this.collection.getTop10TokenOwners(this.tokenId);3811  }38123813  async getBalance(addressObj: ICrossAccountId) {3814    return await this.collection.getTokenBalance(this.tokenId, addressObj);3815  }38163817  async getTotalPieces() {3818    return await this.collection.getTokenTotalPieces(this.tokenId);3819  }38203821  async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) {3822    return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj);3823  }38243825  async transfer(signer: TSigner, addressObj: ICrossAccountId, amount=1n) {3826    return await this.collection.transferToken(signer, this.tokenId, addressObj, amount);3827  }38283829  async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3830    return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj, amount);3831  }38323833  async approve(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3834    return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount);3835  }38363837  async repartition(signer: TSigner, amount: bigint) {3838    return await this.collection.repartitionToken(signer, this.tokenId, amount);3839  }38403841  async burn(signer: TSigner, amount=1n) {3842    return await this.collection.burnToken(signer, this.tokenId, amount);3843  }38443845  async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {3846    return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount);3847  }38483849  scheduleAt<T extends UniqueHelper>(3850    executionBlockNumber: number,3851    options: ISchedulerOptions = {},3852  ) {3853    const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);3854    return new UniqueRFToken(this.tokenId, scheduledCollection);3855  }38563857  scheduleAfter<T extends UniqueHelper>(3858    blocksBeforeExecution: number,3859    options: ISchedulerOptions = {},3860  ) {3861    const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);3862    return new UniqueRFToken(this.tokenId, scheduledCollection);3863  }38643865  getSudo<T extends UniqueHelper>() {3866    return new UniqueRFToken(this.tokenId, this.collection.getSudo<T>());3867  }3868}