git.delta.rocks / unique-network / refs/commits / 396d6c0fcaf0

difftreelog

CORE-37 Metadata update permissions

str-mv2021-07-14parent: #29c61bb.patch.diff
in: master

7 files changed

modifiednode/cli/src/chain_spec.rsdiffbeforeafterboth
--- a/node/cli/src/chain_spec.rs
+++ b/node/cli/src/chain_spec.rs
@@ -196,6 +196,7 @@
 					const_on_chain_schema: vec![],
 					variable_on_chain_schema: vec![],
 					limits: CollectionLimits::default(),
+					meta_update_permission: MetaUpdatePermission::ItemOwner,
 				},
 			)],
 			nft_item_id: vec![],
modifiedpallets/inflation/src/tests.rsdiffbeforeafterboth
--- a/pallets/inflation/src/tests.rs
+++ b/pallets/inflation/src/tests.rs
@@ -31,6 +31,8 @@
 	type ExistentialDeposit = ExistentialDeposit;
 	type WeightInfo = ();
 	type MaxLocks = MaxLocks;
+	type MaxReserves = ();
+	type ReserveIdentifier = [u8; 8];
 }
 
 frame_support::construct_runtime!(
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -41,7 +41,7 @@
 	MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,
 	AccessMode, ChainLimits, Collection, CreateItemData, CollectionLimits, CollectionId,
 	CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType,
-	FungibleItemType, ReFungibleItemType,
+	FungibleItemType, ReFungibleItemType, MetaUpdatePermission,
 };
 use pallet_ethereum::EthereumTransactionSender;
 
@@ -175,6 +175,10 @@
 		BadCreateRefungibleCall,
 		/// Gas limit exceeded
 		OutOfGas,
+		/// Metadata update denied by collection settings 
+		MetadataUpdateDenied,
+		/// Metadata update flag become unmutable with None option
+		MetadataFlagFrozen,
 	}
 }
 
@@ -533,6 +537,7 @@
 				variable_on_chain_schema: Vec::new(),
 				const_on_chain_schema: Vec::new(),
 				limits,
+				meta_update_permission: MetaUpdatePermission::default(),
 			};
 
 			// Add new collection to map
@@ -922,6 +927,37 @@
 			Ok(())
 		}
 
+		// TODO! transaction weight
+		/// Set meta_update_permission value for particular collection
+		///
+		/// # Permissions
+		///
+		/// * Collection Owner.
+		///
+		/// # Arguments
+		///
+		/// * collection_id: ID of the collection.
+		///
+		/// * value: New flag value.
+		#[weight = <T as Config>::WeightInfo::burn_item()]
+		#[transactional]
+		pub fn set_meta_update_permission_flag(origin, collection_id: CollectionId, value: MetaUpdatePermission) -> DispatchResult {
+
+			let sender = ensure_signed(origin)?;
+			let mut target_collection = Self::get_collection(collection_id)?;
+
+			ensure!(
+				target_collection.meta_update_permission != MetaUpdatePermission::None,
+				Error::<T>::MetadataFlagFrozen
+			);
+			Self::check_owner_permissions(&target_collection, &sender)?;
+			
+			target_collection.meta_update_permission = value;
+			Self::save_collection(target_collection);
+
+			Ok(())
+		}
+
 		/// Destroys a concrete instance of NFT.
 		///
 		/// # Permissions
@@ -1481,11 +1517,7 @@
 		);
 
 		// Modify permissions check
-		ensure!(
-			Self::is_item_owner(sender, collection, item_id)
-				|| Self::is_owner_or_admin_permissions(collection, sender),
-			Error::<T>::NoPermission
-		);
+		Self::meta_update_check(sender, collection, item_id)?;
 
 		match collection.mode {
 			CollectionMode::NFT => Self::set_nft_variable_data(collection, item_id, data)?,
@@ -1499,6 +1531,32 @@
 		Ok(())
 	}
 
+	pub fn meta_update_check(		
+		sender: &T::CrossAccountId,
+		collection: &CollectionHandle<T>,
+		item_id: TokenId) -> DispatchResult {
+
+		match collection.meta_update_permission {
+			MetaUpdatePermission::ItemOwner => {
+				ensure!(
+					Self::is_item_owner(sender, collection, item_id), 
+					Error::<T>::NoPermission
+				);
+			},
+			MetaUpdatePermission::Admin => {
+				ensure!(
+					Self::is_owner_or_admin_permissions(collection, sender), 
+					Error::<T>::NoPermission
+				);
+			},
+			MetaUpdatePermission::None => {
+				fail!(Error::<T>::MetadataUpdateDenied);
+			},
+		}
+
+		Ok(())
+	}
+
 	pub fn create_multiple_items_internal(
 		sender: &T::CrossAccountId,
 		collection: &CollectionHandle<T>,
modifiedpallets/nft/src/mock.rsdiffbeforeafterboth
before · pallets/nft/src/mock.rs
1#![allow(clippy::from_over_into)]23use crate as pallet_template;4use sp_core::H256;5use frame_support::{parameter_types, weights::IdentityFee};6use sp_runtime::{7	traits::{BlakeTwo256, IdentityLookup},8	testing::Header,9	Perbill,10};11use pallet_transaction_payment::{CurrencyAdapter};12use frame_system as system;13use pallet_evm::AddressMapping;14use crate::{EvmBackwardsAddressMapping, CrossAccountId};15use codec::{Encode, Decode};1617type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;18type Block = frame_system::mocking::MockBlock<Test>;1920// Configure a mock runtime to test the pallet.21frame_support::construct_runtime!(22	pub enum Test where23		Block = Block,24		NodeBlock = Block,25		UncheckedExtrinsic = UncheckedExtrinsic,26	{27		System: frame_system::{Pallet, Call, Config, Storage, Event<T>},28		TemplateModule: pallet_template::{Pallet, Call, Storage},29		Balances: pallet_balances::{Pallet, Call, Storage},30	}31);3233parameter_types! {34	pub const BlockHashCount: u64 = 250;35	pub const SS58Prefix: u8 = 42;36}3738impl system::Config for Test {39	type BaseCallFilter = ();40	type BlockWeights = ();41	type BlockLength = ();42	type DbWeight = ();43	type Origin = Origin;44	type Call = Call;45	type Index = u64;46	type BlockNumber = u64;47	type Hash = H256;48	type Hashing = BlakeTwo256;49	type AccountId = u64;50	type Lookup = IdentityLookup<Self::AccountId>;51	type Header = Header;52	type Event = ();53	type BlockHashCount = BlockHashCount;54	type Version = ();55	type PalletInfo = PalletInfo;56	type AccountData = pallet_balances::AccountData<u64>;57	type OnNewAccount = ();58	type OnKilledAccount = ();59	type SystemWeightInfo = ();60	type SS58Prefix = SS58Prefix;61	type OnSetCode = ();62}6364parameter_types! {65	pub const ExistentialDeposit: u64 = 1;66	pub const MaxLocks: u32 = 50;67}68//frame_system::Module<Test>;69impl pallet_balances::Config for Test {70	type AccountStore = System;71	type Balance = u64;72	type DustRemoval = ();73	type Event = ();74	type ExistentialDeposit = ExistentialDeposit;75	type WeightInfo = ();76	type MaxLocks = MaxLocks;77}7879parameter_types! {80	pub const TransactionByteFee: u64 = 1;81}8283impl pallet_transaction_payment::Config for Test {84	type OnChargeTransaction = CurrencyAdapter<pallet_balances::Pallet<Test>, ()>;85	type TransactionByteFee = TransactionByteFee;86	type WeightToFee = IdentityFee<u64>;87	type FeeMultiplierUpdate = ();88}8990parameter_types! {91	pub const MinimumPeriod: u64 = 1;92}93impl pallet_timestamp::Config for Test {94	type Moment = u64;95	type OnTimestampSet = ();96	type MinimumPeriod = MinimumPeriod;97	type WeightInfo = ();98}99100type Timestamp = pallet_timestamp::Pallet<Test>;101type Randomness = pallet_randomness_collective_flip::Pallet<Test>;102103parameter_types! {104	pub const TombstoneDeposit: u64 = 1;105	pub const DepositPerContract: u64 = 1;106	pub const DepositPerStorageByte: u64 = 1;107	pub const DepositPerStorageItem: u64 = 1;108	pub RentFraction: Perbill = Perbill::from_rational(1u32, 30 * 24 * 60 * 10);109	pub const SurchargeReward: u64 = 1;110	pub const SignedClaimHandicap: u32 = 2;111	pub DeletionWeightLimit: u64 = u64::MAX;//Perbill::from_percent(10);112	pub DeletionQueueDepth: u32 = 10;113	pub Schedule: pallet_contracts::Schedule<Test> = Default::default();114}115116impl pallet_contracts::Config for Test {117	type Time = Timestamp;118	type Randomness = Randomness;119	type Currency = pallet_balances::Pallet<Test>;120	type Event = ();121	type RentPayment = ();122	type SignedClaimHandicap = SignedClaimHandicap;123	type TombstoneDeposit = TombstoneDeposit;124	type DepositPerContract = DepositPerContract;125	type DepositPerStorageByte = DepositPerStorageByte;126	type DepositPerStorageItem = DepositPerStorageItem;127	type RentFraction = RentFraction;128	type SurchargeReward = SurchargeReward;129	type DeletionWeightLimit = DeletionWeightLimit;130	type DeletionQueueDepth = DeletionQueueDepth;131	type ChainExtension = ();132	type WeightPrice = ();133	type WeightInfo = pallet_contracts::weights::SubstrateWeight<Self>;134	type Schedule = Schedule;135	type CallStack = [pallet_contracts::Frame<Self>; 31];136}137138parameter_types! {139	pub const CollectionCreationPrice: u32 = 0;140	pub TreasuryAccountId: u64 = 1234;141	pub EthereumChainId: u32 = 1111;142}143144pub struct TestEvmAddressMapping;145impl AddressMapping<u64> for TestEvmAddressMapping {146	fn into_account_id(_addr: sp_core::H160) -> u64 {147		unimplemented!()148	}149}150151pub struct TestEvmBackwardsAddressMapping;152impl EvmBackwardsAddressMapping<u64> for TestEvmBackwardsAddressMapping {153	fn from_account_id(_account_id: u64) -> sp_core::H160 {154		unimplemented!()155	}156}157158#[derive(Encode, Decode, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]159pub struct TestCrossAccountId(u64, sp_core::H160);160impl CrossAccountId<u64> for TestCrossAccountId {161	fn from_sub(sub: u64) -> Self {162		let mut eth = [0; 20];163		eth[12..20].copy_from_slice(&sub.to_be_bytes());164		Self(sub, sp_core::H160(eth))165	}166	fn as_sub(&self) -> &u64 {167		&self.0168	}169	fn from_eth(_eth: sp_core::H160) -> Self {170		unimplemented!()171	}172	fn as_eth(&self) -> &sp_core::H160 {173		&self.1174	}175}176177pub struct TestEtheremTransactionSender;178impl pallet_ethereum::EthereumTransactionSender for TestEtheremTransactionSender {179	fn submit_logs_transaction(180		_tx: pallet_ethereum::Transaction,181		_logs: Vec<pallet_ethereum::Log>,182	) -> Result<(), sp_runtime::DispatchError> {183		Ok(())184	}185}186187impl pallet_template::Config for Test {188	type Event = ();189	type WeightInfo = ();190	type CollectionCreationPrice = CollectionCreationPrice;191	type Currency = pallet_balances::Pallet<Test>;192	type TreasuryAccountId = TreasuryAccountId;193	type EvmAddressMapping = TestEvmAddressMapping;194	type EvmBackwardsAddressMapping = TestEvmBackwardsAddressMapping;195	type CrossAccountId = TestCrossAccountId;196	type EthereumChainId = EthereumChainId;197	type EthereumTransactionSender = TestEtheremTransactionSender;198}199200// Build genesis storage according to the mock runtime.201pub fn new_test_ext() -> sp_io::TestExternalities {202	system::GenesisConfig::default()203		.build_storage::<Test>()204		.unwrap()205		.into()206}
after · pallets/nft/src/mock.rs
1#![allow(clippy::from_over_into)]23use crate as pallet_template;4use sp_core::H256;5use frame_support::{parameter_types, weights::IdentityFee};6use sp_runtime::{7	traits::{BlakeTwo256, IdentityLookup},8	testing::Header,9	Perbill,10};11use pallet_transaction_payment::{CurrencyAdapter};12use frame_system as system;13use pallet_evm::AddressMapping;14use crate::{EvmBackwardsAddressMapping, CrossAccountId};15use codec::{Encode, Decode};1617type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;18type Block = frame_system::mocking::MockBlock<Test>;1920// Configure a mock runtime to test the pallet.21frame_support::construct_runtime!(22	pub enum Test where23		Block = Block,24		NodeBlock = Block,25		UncheckedExtrinsic = UncheckedExtrinsic,26	{27		System: frame_system::{Pallet, Call, Config, Storage, Event<T>},28		TemplateModule: pallet_template::{Pallet, Call, Storage},29		Balances: pallet_balances::{Pallet, Call, Storage},30	}31);3233parameter_types! {34	pub const BlockHashCount: u64 = 250;35	pub const SS58Prefix: u8 = 42;36}3738impl system::Config for Test {39	type BaseCallFilter = ();40	type BlockWeights = ();41	type BlockLength = ();42	type DbWeight = ();43	type Origin = Origin;44	type Call = Call;45	type Index = u64;46	type BlockNumber = u64;47	type Hash = H256;48	type Hashing = BlakeTwo256;49	type AccountId = u64;50	type Lookup = IdentityLookup<Self::AccountId>;51	type Header = Header;52	type Event = ();53	type BlockHashCount = BlockHashCount;54	type Version = ();55	type PalletInfo = PalletInfo;56	type AccountData = pallet_balances::AccountData<u64>;57	type OnNewAccount = ();58	type OnKilledAccount = ();59	type SystemWeightInfo = ();60	type SS58Prefix = SS58Prefix;61	type OnSetCode = ();62}6364parameter_types! {65	pub const ExistentialDeposit: u64 = 1;66	pub const MaxLocks: u32 = 50;67}68//frame_system::Module<Test>;69impl pallet_balances::Config for Test {70	type AccountStore = System;71	type Balance = u64;72	type DustRemoval = ();73	type Event = ();74	type ExistentialDeposit = ExistentialDeposit;75	type WeightInfo = ();76	type MaxLocks = MaxLocks;77	type MaxReserves = ();78	type ReserveIdentifier = [u8; 8];79}8081parameter_types! {82	pub const TransactionByteFee: u64 = 1;83}8485impl pallet_transaction_payment::Config for Test {86	type OnChargeTransaction = CurrencyAdapter<pallet_balances::Pallet<Test>, ()>;87	type TransactionByteFee = TransactionByteFee;88	type WeightToFee = IdentityFee<u64>;89	type FeeMultiplierUpdate = ();90}9192parameter_types! {93	pub const MinimumPeriod: u64 = 1;94}95impl pallet_timestamp::Config for Test {96	type Moment = u64;97	type OnTimestampSet = ();98	type MinimumPeriod = MinimumPeriod;99	type WeightInfo = ();100}101102type Timestamp = pallet_timestamp::Pallet<Test>;103type Randomness = pallet_randomness_collective_flip::Pallet<Test>;104105parameter_types! {106	pub const CollectionCreationPrice: u32 = 0;107	pub TreasuryAccountId: u64 = 1234;108	pub EthereumChainId: u32 = 1111;109}110111pub struct TestEvmAddressMapping;112impl AddressMapping<u64> for TestEvmAddressMapping {113	fn into_account_id(_addr: sp_core::H160) -> u64 {114		unimplemented!()115	}116}117118pub struct TestEvmBackwardsAddressMapping;119impl EvmBackwardsAddressMapping<u64> for TestEvmBackwardsAddressMapping {120	fn from_account_id(_account_id: u64) -> sp_core::H160 {121		unimplemented!()122	}123}124125#[derive(Encode, Decode, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]126pub struct TestCrossAccountId(u64, sp_core::H160);127impl CrossAccountId<u64> for TestCrossAccountId {128	fn from_sub(sub: u64) -> Self {129		let mut eth = [0; 20];130		eth[12..20].copy_from_slice(&sub.to_be_bytes());131		Self(sub, sp_core::H160(eth))132	}133	fn as_sub(&self) -> &u64 {134		&self.0135	}136	fn from_eth(_eth: sp_core::H160) -> Self {137		unimplemented!()138	}139	fn as_eth(&self) -> &sp_core::H160 {140		&self.1141	}142}143144pub struct TestEtheremTransactionSender;145impl pallet_ethereum::EthereumTransactionSender for TestEtheremTransactionSender {146	fn submit_logs_transaction(147		_tx: pallet_ethereum::Transaction,148		_logs: Vec<pallet_ethereum::Log>,149	) -> Result<(), sp_runtime::DispatchError> {150		Ok(())151	}152}153154impl pallet_template::Config for Test {155	type Event = ();156	type WeightInfo = ();157	type CollectionCreationPrice = CollectionCreationPrice;158	type Currency = pallet_balances::Pallet<Test>;159	type TreasuryAccountId = TreasuryAccountId;160	type EvmAddressMapping = TestEvmAddressMapping;161	type EvmBackwardsAddressMapping = TestEvmBackwardsAddressMapping;162	type CrossAccountId = TestCrossAccountId;163	type EthereumChainId = EthereumChainId;164	type EthereumTransactionSender = TestEtheremTransactionSender;165}166167// Build genesis storage according to the mock runtime.168pub fn new_test_ext() -> sp_io::TestExternalities {169	system::GenesisConfig::default()170		.build_storage::<Test>()171		.unwrap()172		.into()173}
modifiedpallets/nft/src/tests.rsdiffbeforeafterboth
--- a/pallets/nft/src/tests.rs
+++ b/pallets/nft/src/tests.rs
@@ -2351,3 +2351,200 @@
 		);
 	});
 }
+
+#[test]
+fn set_variable_meta_data_on_nft_with_item_owner_permission_flag() {
+	new_test_ext().execute_with(|| {
+		default_limits();
+
+		let collection_id = create_test_collection(&CollectionMode::NFT, 1);
+
+		let origin1 = Origin::signed(1);
+
+		let data = default_nft_data();
+		create_test_item(1, &data.into());
+
+		TemplateModule::set_meta_update_permission_flag(origin1.clone(), collection_id, MetaUpdatePermission::ItemOwner);
+
+		let variable_data = b"test set_variable_meta_data method.".to_vec();
+		assert_ok!(TemplateModule::set_variable_meta_data(
+			origin1,
+			collection_id,
+			1,
+			variable_data.clone()
+		));
+
+		assert_eq!(
+			TemplateModule::nft_item_id(collection_id, 1)
+				.unwrap()
+				.variable_data,
+			variable_data
+		);
+	});
+}
+
+#[test]
+fn set_variable_meta_data_on_nft_with_item_owner_permission_flag_neg() {
+	new_test_ext().execute_with(|| {
+		default_limits();
+
+		let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 2, 1);
+
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		assert_ok!(TemplateModule::set_mint_permission(
+			origin2.clone(),
+			collection_id,
+			true
+		));
+		assert_ok!(TemplateModule::add_to_white_list(
+			origin2.clone(),
+			collection_id,
+			account(1)
+		));
+
+		let data = default_nft_data();
+		create_test_item(1, &data.into());
+
+		TemplateModule::set_meta_update_permission_flag(origin2.clone(), collection_id, MetaUpdatePermission::ItemOwner);
+
+		let variable_data = b"test set_variable_meta_data method.".to_vec();
+		assert_noop!(TemplateModule::set_variable_meta_data(
+			origin2,
+			collection_id,
+			1,
+			variable_data.clone()),
+		Error::<Test>::NoPermission
+		);
+	});
+}
+
+#[test]
+fn set_variable_meta_data_on_nft_with_admin_flag() {
+	new_test_ext().execute_with(|| {
+		default_limits();
+
+		let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 2, 1);
+
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		assert_ok!(TemplateModule::set_mint_permission(
+			origin2.clone(),
+			collection_id,
+			true
+		));
+		assert_ok!(TemplateModule::add_to_white_list(
+			origin2.clone(),
+			collection_id,
+			account(1)
+		));
+
+		assert_ok!(TemplateModule::add_collection_admin(
+			origin2.clone(),
+			collection_id,
+			account(1)
+		));
+
+		let data = default_nft_data();
+		create_test_item(1, &data.into());
+
+		TemplateModule::set_meta_update_permission_flag(origin2.clone(), collection_id, MetaUpdatePermission::Admin);
+
+		let variable_data = b"test set_variable_meta_data method.".to_vec();
+		assert_ok!(TemplateModule::set_variable_meta_data(
+			origin1,
+			collection_id,
+			1,
+			variable_data.clone()
+		));
+
+		assert_eq!(
+			TemplateModule::nft_item_id(collection_id, 1)
+				.unwrap()
+				.variable_data,
+			variable_data
+		);
+	});
+}
+
+#[test]
+fn set_variable_meta_data_on_nft_with_admin_flag_neg() {
+	new_test_ext().execute_with(|| {
+		default_limits();
+
+		let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 2, 1);
+
+		let origin1 = Origin::signed(1);
+		let origin2 = Origin::signed(2);
+
+		assert_ok!(TemplateModule::set_mint_permission(
+			origin2.clone(),
+			collection_id,
+			true
+		));
+		assert_ok!(TemplateModule::add_to_white_list(
+			origin2.clone(),
+			collection_id,
+			account(1)
+		));
+
+		let data = default_nft_data();
+		create_test_item(1, &data.into());
+
+		TemplateModule::set_meta_update_permission_flag(origin2.clone(), collection_id, MetaUpdatePermission::Admin);
+
+		let variable_data = b"test set_variable_meta_data method.".to_vec();
+		assert_noop!(TemplateModule::set_variable_meta_data(
+			origin1,
+			collection_id,
+			1,
+			variable_data.clone()),
+		Error::<Test>::NoPermission
+		);
+	});
+}
+
+#[test]
+fn set_variable_meta_flag_after_freeze() {
+	new_test_ext().execute_with(|| {
+		default_limits();
+
+		let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 2, 1);
+
+		let origin2 = Origin::signed(2);
+
+		TemplateModule::set_meta_update_permission_flag(origin2.clone(), collection_id, MetaUpdatePermission::None);
+		assert_noop!(TemplateModule::set_meta_update_permission_flag(
+			origin2.clone(),
+			collection_id,
+			MetaUpdatePermission::Admin),
+		Error::<Test>::MetadataFlagFrozen
+		);
+	});
+}
+
+#[test]
+fn set_variable_meta_data_on_nft_with_none_flag_neg() {
+	new_test_ext().execute_with(|| {
+		default_limits();
+
+		let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, 1, 1);
+		let origin1 = Origin::signed(1);
+
+		let data = default_nft_data();
+		create_test_item(1, &data.into());
+
+		TemplateModule::set_meta_update_permission_flag(origin1.clone(), collection_id, MetaUpdatePermission::None);
+
+		let variable_data = b"test set_variable_meta_data method.".to_vec();
+		assert_noop!(TemplateModule::set_variable_meta_data(
+			origin1.clone(),
+			collection_id,
+			1,
+			variable_data.clone()),
+		Error::<Test>::MetadataUpdateDenied
+		);
+	});
+}
\ No newline at end of file
modifiedpallets/scheduler/src/lib.rsdiffbeforeafterboth
--- a/pallets/scheduler/src/lib.rs
+++ b/pallets/scheduler/src/lib.rs
@@ -947,900 +947,4 @@
 	fn root() -> OriginCaller {
 		system::RawOrigin::Root.into()
 	}
-
-	#[test]
-	fn basic_scheduling_works() {
-		new_test_ext().execute_with(|| {
-			let call = Call::Logger(logger::Call::log(42, 1000));
-			assert!(!<Test as frame_system::Config>::BaseCallFilter::filter(
-				&call
-			));
-			assert_ok!(Scheduler::do_schedule(
-				DispatchTime::At(4),
-				None,
-				127,
-				root(),
-				call
-			));
-			run_to_block(3);
-			assert!(logger::log().is_empty());
-			run_to_block(4);
-			assert_eq!(logger::log(), vec![(root(), 42u32)]);
-			run_to_block(100);
-			assert_eq!(logger::log(), vec![(root(), 42u32)]);
-		});
-	}
-
-	#[test]
-	fn schedule_after_works() {
-		new_test_ext().execute_with(|| {
-			run_to_block(2);
-			let call = Call::Logger(logger::Call::log(42, 1000));
-			assert!(!<Test as frame_system::Config>::BaseCallFilter::filter(
-				&call
-			));
-			// This will schedule the call 3 blocks after the next block... so block 3 + 3 = 6
-			assert_ok!(Scheduler::do_schedule(
-				DispatchTime::After(3),
-				None,
-				127,
-				root(),
-				call
-			));
-			run_to_block(5);
-			assert!(logger::log().is_empty());
-			run_to_block(6);
-			assert_eq!(logger::log(), vec![(root(), 42u32)]);
-			run_to_block(100);
-			assert_eq!(logger::log(), vec![(root(), 42u32)]);
-		});
-	}
-
-	#[test]
-	fn schedule_after_zero_works() {
-		new_test_ext().execute_with(|| {
-			run_to_block(2);
-			let call = Call::Logger(logger::Call::log(42, 1000));
-			assert!(!<Test as frame_system::Config>::BaseCallFilter::filter(
-				&call
-			));
-			assert_ok!(Scheduler::do_schedule(
-				DispatchTime::After(0),
-				None,
-				127,
-				root(),
-				call
-			));
-			// Will trigger on the next block.
-			run_to_block(3);
-			assert_eq!(logger::log(), vec![(root(), 42u32)]);
-			run_to_block(100);
-			assert_eq!(logger::log(), vec![(root(), 42u32)]);
-		});
-	}
-
-	#[test]
-	fn periodic_scheduling_works() {
-		new_test_ext().execute_with(|| {
-			// at #4, every 3 blocks, 3 times.
-			assert_ok!(Scheduler::do_schedule(
-				DispatchTime::At(4),
-				Some((3, 3)),
-				127,
-				root(),
-				Call::Logger(logger::Call::log(42, 1000))
-			));
-			run_to_block(3);
-			assert!(logger::log().is_empty());
-			run_to_block(4);
-			assert_eq!(logger::log(), vec![(root(), 42u32)]);
-			run_to_block(6);
-			assert_eq!(logger::log(), vec![(root(), 42u32)]);
-			run_to_block(7);
-			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);
-			run_to_block(9);
-			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);
-			run_to_block(10);
-			assert_eq!(
-				logger::log(),
-				vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]
-			);
-			run_to_block(100);
-			assert_eq!(
-				logger::log(),
-				vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]
-			);
-		});
-	}
-
-	#[test]
-	fn reschedule_works() {
-		new_test_ext().execute_with(|| {
-			let call = Call::Logger(logger::Call::log(42, 1000));
-			assert!(!<Test as frame_system::Config>::BaseCallFilter::filter(
-				&call
-			));
-			assert_eq!(
-				Scheduler::do_schedule(DispatchTime::At(4), None, 127, root(), call).unwrap(),
-				(4, 0)
-			);
-
-			run_to_block(3);
-			assert!(logger::log().is_empty());
-
-			assert_eq!(
-				Scheduler::do_reschedule((4, 0), DispatchTime::At(6)).unwrap(),
-				(6, 0)
-			);
-
-			assert_noop!(
-				Scheduler::do_reschedule((6, 0), DispatchTime::At(6)),
-				Error::<Test>::RescheduleNoChange
-			);
-
-			run_to_block(4);
-			assert!(logger::log().is_empty());
-
-			run_to_block(6);
-			assert_eq!(logger::log(), vec![(root(), 42u32)]);
-
-			run_to_block(100);
-			assert_eq!(logger::log(), vec![(root(), 42u32)]);
-		});
-	}
-
-	#[test]
-	fn reschedule_named_works() {
-		new_test_ext().execute_with(|| {
-			let call = Call::Logger(logger::Call::log(42, 1000));
-			assert!(!<Test as frame_system::Config>::BaseCallFilter::filter(
-				&call
-			));
-			assert_eq!(
-				Scheduler::do_schedule_named(
-					1u32.encode(),
-					DispatchTime::At(4),
-					None,
-					127,
-					root(),
-					call
-				)
-				.unwrap(),
-				(4, 0)
-			);
-
-			run_to_block(3);
-			assert!(logger::log().is_empty());
-
-			assert_eq!(
-				Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(6)).unwrap(),
-				(6, 0)
-			);
-
-			assert_noop!(
-				Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(6)),
-				Error::<Test>::RescheduleNoChange
-			);
-
-			run_to_block(4);
-			assert!(logger::log().is_empty());
-
-			run_to_block(6);
-			assert_eq!(logger::log(), vec![(root(), 42u32)]);
-
-			run_to_block(100);
-			assert_eq!(logger::log(), vec![(root(), 42u32)]);
-		});
-	}
-
-	#[test]
-	fn reschedule_named_perodic_works() {
-		new_test_ext().execute_with(|| {
-			let call = Call::Logger(logger::Call::log(42, 1000));
-			assert!(!<Test as frame_system::Config>::BaseCallFilter::filter(
-				&call
-			));
-			assert_eq!(
-				Scheduler::do_schedule_named(
-					1u32.encode(),
-					DispatchTime::At(4),
-					Some((3, 3)),
-					127,
-					root(),
-					call
-				)
-				.unwrap(),
-				(4, 0)
-			);
-
-			run_to_block(3);
-			assert!(logger::log().is_empty());
-
-			assert_eq!(
-				Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(5)).unwrap(),
-				(5, 0)
-			);
-			assert_eq!(
-				Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(6)).unwrap(),
-				(6, 0)
-			);
-
-			run_to_block(5);
-			assert!(logger::log().is_empty());
-
-			run_to_block(6);
-			assert_eq!(logger::log(), vec![(root(), 42u32)]);
-
-			assert_eq!(
-				Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(10)).unwrap(),
-				(10, 0)
-			);
-
-			run_to_block(9);
-			assert_eq!(logger::log(), vec![(root(), 42u32)]);
-
-			run_to_block(10);
-			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);
-
-			run_to_block(13);
-			assert_eq!(
-				logger::log(),
-				vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]
-			);
-
-			run_to_block(100);
-			assert_eq!(
-				logger::log(),
-				vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]
-			);
-		});
-	}
-
-	#[test]
-	fn cancel_named_scheduling_works_with_normal_cancel() {
-		new_test_ext().execute_with(|| {
-			// at #4.
-			Scheduler::do_schedule_named(
-				1u32.encode(),
-				DispatchTime::At(4),
-				None,
-				127,
-				root(),
-				Call::Logger(logger::Call::log(69, 1000)),
-			)
-			.unwrap();
-			let i = Scheduler::do_schedule(
-				DispatchTime::At(4),
-				None,
-				127,
-				root(),
-				Call::Logger(logger::Call::log(42, 1000)),
-			)
-			.unwrap();
-			run_to_block(3);
-			assert!(logger::log().is_empty());
-			assert_ok!(Scheduler::do_cancel_named(None, 1u32.encode()));
-			assert_ok!(Scheduler::do_cancel(None, i));
-			run_to_block(100);
-			assert!(logger::log().is_empty());
-		});
-	}
-
-	#[test]
-	fn cancel_named_periodic_scheduling_works() {
-		new_test_ext().execute_with(|| {
-			// at #4, every 3 blocks, 3 times.
-			Scheduler::do_schedule_named(
-				1u32.encode(),
-				DispatchTime::At(4),
-				Some((3, 3)),
-				127,
-				root(),
-				Call::Logger(logger::Call::log(42, 1000)),
-			)
-			.unwrap();
-			// same id results in error.
-			assert!(Scheduler::do_schedule_named(
-				1u32.encode(),
-				DispatchTime::At(4),
-				None,
-				127,
-				root(),
-				Call::Logger(logger::Call::log(69, 1000))
-			)
-			.is_err());
-			// different id is ok.
-			Scheduler::do_schedule_named(
-				2u32.encode(),
-				DispatchTime::At(8),
-				None,
-				127,
-				root(),
-				Call::Logger(logger::Call::log(69, 1000)),
-			)
-			.unwrap();
-			run_to_block(3);
-			assert!(logger::log().is_empty());
-			run_to_block(4);
-			assert_eq!(logger::log(), vec![(root(), 42u32)]);
-			run_to_block(6);
-			assert_ok!(Scheduler::do_cancel_named(None, 1u32.encode()));
-			run_to_block(100);
-			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]);
-		});
-	}
-
-	#[test]
-	fn scheduler_respects_weight_limits() {
-		new_test_ext().execute_with(|| {
-			assert_ok!(Scheduler::do_schedule(
-				DispatchTime::At(4),
-				None,
-				127,
-				root(),
-				Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2))
-			));
-			assert_ok!(Scheduler::do_schedule(
-				DispatchTime::At(4),
-				None,
-				127,
-				root(),
-				Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))
-			));
-			// 69 and 42 do not fit together
-			run_to_block(4);
-			assert_eq!(logger::log(), vec![(root(), 42u32)]);
-			run_to_block(5);
-			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]);
-		});
-	}
-
-	#[test]
-	fn scheduler_respects_hard_deadlines_more() {
-		new_test_ext().execute_with(|| {
-			assert_ok!(Scheduler::do_schedule(
-				DispatchTime::At(4),
-				None,
-				0,
-				root(),
-				Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2))
-			));
-			assert_ok!(Scheduler::do_schedule(
-				DispatchTime::At(4),
-				None,
-				0,
-				root(),
-				Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))
-			));
-			// With base weights, 69 and 42 should not fit together, but do because of hard deadlines
-			run_to_block(4);
-			assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]);
-		});
-	}
-
-	#[test]
-	fn scheduler_respects_priority_ordering() {
-		new_test_ext().execute_with(|| {
-			assert_ok!(Scheduler::do_schedule(
-				DispatchTime::At(4),
-				None,
-				1,
-				root(),
-				Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2))
-			));
-			assert_ok!(Scheduler::do_schedule(
-				DispatchTime::At(4),
-				None,
-				0,
-				root(),
-				Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))
-			));
-			run_to_block(4);
-			assert_eq!(logger::log(), vec![(root(), 69u32), (root(), 42u32)]);
-		});
-	}
-
-	#[test]
-	fn scheduler_respects_priority_ordering_with_soft_deadlines() {
-		new_test_ext().execute_with(|| {
-			assert_ok!(Scheduler::do_schedule(
-				DispatchTime::At(4),
-				None,
-				255,
-				root(),
-				Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 3))
-			));
-			assert_ok!(Scheduler::do_schedule(
-				DispatchTime::At(4),
-				None,
-				127,
-				root(),
-				Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))
-			));
-			assert_ok!(Scheduler::do_schedule(
-				DispatchTime::At(4),
-				None,
-				126,
-				root(),
-				Call::Logger(logger::Call::log(2600, MaximumSchedulerWeight::get() / 2))
-			));
-
-			// 2600 does not fit with 69 or 42, but has higher priority, so will go through
-			run_to_block(4);
-			assert_eq!(logger::log(), vec![(root(), 2600u32)]);
-			// 69 and 42 fit together
-			run_to_block(5);
-			assert_eq!(
-				logger::log(),
-				vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32)]
-			);
-		});
-	}
-
-	#[test]
-	fn on_initialize_weight_is_correct() {
-		new_test_ext().execute_with(|| {
-			let base_weight: Weight =
-				<Test as frame_system::Config>::DbWeight::get().reads_writes(1, 2);
-			let base_multiplier = 0;
-			let named_multiplier = <Test as frame_system::Config>::DbWeight::get().writes(1);
-			let periodic_multiplier =
-				<Test as frame_system::Config>::DbWeight::get().reads_writes(1, 1);
-
-			// Named
-			assert_ok!(Scheduler::do_schedule_named(
-				1u32.encode(),
-				DispatchTime::At(1),
-				None,
-				255,
-				root(),
-				Call::Logger(logger::Call::log(3, MaximumSchedulerWeight::get() / 3))
-			));
-			// Anon Periodic
-			assert_ok!(Scheduler::do_schedule(
-				DispatchTime::At(1),
-				Some((1000, 3)),
-				128,
-				root(),
-				Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 3))
-			));
-			// Anon
-			assert_ok!(Scheduler::do_schedule(
-				DispatchTime::At(1),
-				None,
-				127,
-				root(),
-				Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))
-			));
-			// Named Periodic
-			assert_ok!(Scheduler::do_schedule_named(
-				2u32.encode(),
-				DispatchTime::At(1),
-				Some((1000, 3)),
-				126,
-				root(),
-				Call::Logger(logger::Call::log(2600, MaximumSchedulerWeight::get() / 2))
-			));
-
-			// Will include the named periodic only
-			let actual_weight = Scheduler::on_initialize(1);
-			let call_weight = MaximumSchedulerWeight::get() / 2;
-			assert_eq!(
-				actual_weight,
-				call_weight
-					+ base_weight + base_multiplier
-					+ named_multiplier + periodic_multiplier
-			);
-			assert_eq!(logger::log(), vec![(root(), 2600u32)]);
-
-			// Will include anon and anon periodic
-			let actual_weight = Scheduler::on_initialize(2);
-			let call_weight = MaximumSchedulerWeight::get() / 2 + MaximumSchedulerWeight::get() / 3;
-			assert_eq!(
-				actual_weight,
-				call_weight + base_weight + base_multiplier * 2 + periodic_multiplier
-			);
-			assert_eq!(
-				logger::log(),
-				vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32)]
-			);
-
-			// Will include named only
-			let actual_weight = Scheduler::on_initialize(3);
-			let call_weight = MaximumSchedulerWeight::get() / 3;
-			assert_eq!(
-				actual_weight,
-				call_weight + base_weight + base_multiplier + named_multiplier
-			);
-			assert_eq!(
-				logger::log(),
-				vec![
-					(root(), 2600u32),
-					(root(), 69u32),
-					(root(), 42u32),
-					(root(), 3u32)
-				]
-			);
-
-			// Will contain none
-			let actual_weight = Scheduler::on_initialize(4);
-			assert_eq!(actual_weight, 0);
-		});
-	}
-
-	#[test]
-	fn root_calls_works() {
-		new_test_ext().execute_with(|| {
-			let call = Box::new(Call::Logger(logger::Call::log(69, 1000)));
-			let call2 = Box::new(Call::Logger(logger::Call::log(42, 1000)));
-			assert_ok!(Scheduler::schedule_named(
-				Origin::root(),
-				1u32.encode(),
-				4,
-				None,
-				127,
-				call
-			));
-			assert_ok!(Scheduler::schedule(Origin::root(), 4, None, 127, call2));
-			run_to_block(3);
-			// Scheduled calls are in the agenda.
-			assert_eq!(Agenda::<Test>::get(4).len(), 2);
-			assert!(logger::log().is_empty());
-			assert_ok!(Scheduler::cancel_named(Origin::root(), 1u32.encode()));
-			assert_ok!(Scheduler::cancel(Origin::root(), 4, 1));
-			// Scheduled calls are made NONE, so should not effect state
-			run_to_block(100);
-			assert!(logger::log().is_empty());
-		});
-	}
-
-	#[test]
-	fn fails_to_schedule_task_in_the_past() {
-		new_test_ext().execute_with(|| {
-			run_to_block(3);
-
-			let call = Box::new(Call::Logger(logger::Call::log(69, 1000)));
-			let call2 = Box::new(Call::Logger(logger::Call::log(42, 1000)));
-
-			assert_err!(
-				Scheduler::schedule_named(Origin::root(), 1u32.encode(), 2, None, 127, call),
-				Error::<Test>::TargetBlockNumberInPast,
-			);
-
-			assert_err!(
-				Scheduler::schedule(Origin::root(), 2, None, 127, call2.clone()),
-				Error::<Test>::TargetBlockNumberInPast,
-			);
-
-			assert_err!(
-				Scheduler::schedule(Origin::root(), 3, None, 127, call2),
-				Error::<Test>::TargetBlockNumberInPast,
-			);
-		});
-	}
-
-	#[test]
-	fn should_use_orign() {
-		new_test_ext().execute_with(|| {
-			let call = Box::new(Call::Logger(logger::Call::log(69, 1000)));
-			let call2 = Box::new(Call::Logger(logger::Call::log(42, 1000)));
-			assert_ok!(Scheduler::schedule_named(
-				system::RawOrigin::Signed(1).into(),
-				1u32.encode(),
-				4,
-				None,
-				127,
-				call
-			));
-			assert_ok!(Scheduler::schedule(
-				system::RawOrigin::Signed(1).into(),
-				4,
-				None,
-				127,
-				call2
-			));
-			run_to_block(3);
-			// Scheduled calls are in the agenda.
-			assert_eq!(Agenda::<Test>::get(4).len(), 2);
-			assert!(logger::log().is_empty());
-			assert_ok!(Scheduler::cancel_named(
-				system::RawOrigin::Signed(1).into(),
-				1u32.encode()
-			));
-			assert_ok!(Scheduler::cancel(system::RawOrigin::Signed(1).into(), 4, 1));
-			// Scheduled calls are made NONE, so should not effect state
-			run_to_block(100);
-			assert!(logger::log().is_empty());
-		});
-	}
-
-	#[test]
-	fn should_check_orign() {
-		new_test_ext().execute_with(|| {
-			let call = Box::new(Call::Logger(logger::Call::log(69, 1000)));
-			let call2 = Box::new(Call::Logger(logger::Call::log(42, 1000)));
-			assert_noop!(
-				Scheduler::schedule_named(
-					system::RawOrigin::Signed(2).into(),
-					1u32.encode(),
-					4,
-					None,
-					127,
-					call
-				),
-				BadOrigin
-			);
-			assert_noop!(
-				Scheduler::schedule(system::RawOrigin::Signed(2).into(), 4, None, 127, call2),
-				BadOrigin
-			);
-		});
-	}
-
-	#[test]
-	fn should_check_orign_for_cancel() {
-		new_test_ext().execute_with(|| {
-			let call = Box::new(Call::Logger(logger::Call::log_without_filter(69, 1000)));
-			let call2 = Box::new(Call::Logger(logger::Call::log_without_filter(42, 1000)));
-			assert_ok!(Scheduler::schedule_named(
-				system::RawOrigin::Signed(1).into(),
-				1u32.encode(),
-				4,
-				None,
-				127,
-				call
-			));
-			assert_ok!(Scheduler::schedule(
-				system::RawOrigin::Signed(1).into(),
-				4,
-				None,
-				127,
-				call2
-			));
-			run_to_block(3);
-			// Scheduled calls are in the agenda.
-			assert_eq!(Agenda::<Test>::get(4).len(), 2);
-			assert!(logger::log().is_empty());
-			assert_noop!(
-				Scheduler::cancel_named(system::RawOrigin::Signed(2).into(), 1u32.encode()),
-				BadOrigin
-			);
-			assert_noop!(
-				Scheduler::cancel(system::RawOrigin::Signed(2).into(), 4, 1),
-				BadOrigin
-			);
-			assert_noop!(
-				Scheduler::cancel_named(system::RawOrigin::Root.into(), 1u32.encode()),
-				BadOrigin
-			);
-			assert_noop!(
-				Scheduler::cancel(system::RawOrigin::Root.into(), 4, 1),
-				BadOrigin
-			);
-			run_to_block(5);
-			assert_eq!(
-				logger::log(),
-				vec![
-					(system::RawOrigin::Signed(1).into(), 69u32),
-					(system::RawOrigin::Signed(1).into(), 42u32)
-				]
-			);
-		});
-	}
-
-	#[test]
-	fn migration_to_v2_works() {
-		new_test_ext().execute_with(|| {
-			for i in 0..3u64 {
-				let k = i.twox_64_concat();
-				let old = vec![
-					Some(ScheduledV1 {
-						maybe_id: None,
-						priority: i as u8 + 10,
-						call: Call::Logger(logger::Call::log(96, 100)),
-						maybe_periodic: None,
-					}),
-					None,
-					Some(ScheduledV1 {
-						maybe_id: Some(b"test".to_vec()),
-						priority: 123,
-						call: Call::Logger(logger::Call::log(69, 1000)),
-						maybe_periodic: Some((456u64, 10)),
-					}),
-				];
-				frame_support::migration::put_storage_value(b"Scheduler", b"Agenda", &k, old);
-			}
-
-			assert_eq!(StorageVersion::get(), Releases::V1);
-
-			assert!(Scheduler::migrate_v1_to_t2());
-
-			assert_eq_uvec!(
-				Agenda::<Test>::iter().collect::<Vec<_>>(),
-				vec![
-					(
-						0,
-						vec![
-							Some(ScheduledV2 {
-								maybe_id: None,
-								priority: 10,
-								call: Call::Logger(logger::Call::log(96, 100)),
-								maybe_periodic: None,
-								origin: root(),
-								_phantom: PhantomData::<u64>::default(),
-							}),
-							None,
-							Some(ScheduledV2 {
-								maybe_id: Some(b"test".to_vec()),
-								priority: 123,
-								call: Call::Logger(logger::Call::log(69, 1000)),
-								maybe_periodic: Some((456u64, 10)),
-								origin: root(),
-								_phantom: PhantomData::<u64>::default(),
-							}),
-						]
-					),
-					(
-						1,
-						vec![
-							Some(ScheduledV2 {
-								maybe_id: None,
-								priority: 11,
-								call: Call::Logger(logger::Call::log(96, 100)),
-								maybe_periodic: None,
-								origin: root(),
-								_phantom: PhantomData::<u64>::default(),
-							}),
-							None,
-							Some(ScheduledV2 {
-								maybe_id: Some(b"test".to_vec()),
-								priority: 123,
-								call: Call::Logger(logger::Call::log(69, 1000)),
-								maybe_periodic: Some((456u64, 10)),
-								origin: root(),
-								_phantom: PhantomData::<u64>::default(),
-							}),
-						]
-					),
-					(
-						2,
-						vec![
-							Some(ScheduledV2 {
-								maybe_id: None,
-								priority: 12,
-								call: Call::Logger(logger::Call::log(96, 100)),
-								maybe_periodic: None,
-								origin: root(),
-								_phantom: PhantomData::<u64>::default(),
-							}),
-							None,
-							Some(ScheduledV2 {
-								maybe_id: Some(b"test".to_vec()),
-								priority: 123,
-								call: Call::Logger(logger::Call::log(69, 1000)),
-								maybe_periodic: Some((456u64, 10)),
-								origin: root(),
-								_phantom: PhantomData::<u64>::default(),
-							}),
-						]
-					)
-				]
-			);
-
-			assert_eq!(StorageVersion::get(), Releases::V2);
-		});
-	}
-
-	#[test]
-	fn test_migrate_origin() {
-		new_test_ext().execute_with(|| {
-			for i in 0..3u64 {
-				let k = i.twox_64_concat();
-				let old: Vec<Option<Scheduled<_, _, u32, u64>>> = vec![
-					Some(Scheduled {
-						maybe_id: None,
-						priority: i as u8 + 10,
-						call: Call::Logger(logger::Call::log(96, 100)),
-						origin: 3u32,
-						maybe_periodic: None,
-						_phantom: Default::default(),
-					}),
-					None,
-					Some(Scheduled {
-						maybe_id: Some(b"test".to_vec()),
-						priority: 123,
-						origin: 2u32,
-						call: Call::Logger(logger::Call::log(69, 1000)),
-						maybe_periodic: Some((456u64, 10)),
-						_phantom: Default::default(),
-					}),
-				];
-				frame_support::migration::put_storage_value(b"Scheduler", b"Agenda", &k, old);
-			}
-
-			impl From<u32> for OriginCaller {
-				fn from(value: u32) -> Self {
-					match value {
-						3 => system::RawOrigin::Root.into(),
-						2 => system::RawOrigin::None.into(),
-						_ => unimplemented!(),
-					}
-				}
-			}
-
-			Scheduler::migrate_origin::<u32>();
-
-			assert_eq_uvec!(
-				Agenda::<Test>::iter().collect::<Vec<_>>(),
-				vec![
-					(
-						0,
-						vec![
-							Some(ScheduledV2::<_, _, OriginCaller, u64> {
-								maybe_id: None,
-								priority: 10,
-								call: Call::Logger(logger::Call::log(96, 100)),
-								maybe_periodic: None,
-								origin: system::RawOrigin::Root.into(),
-								_phantom: PhantomData::<u64>::default(),
-							}),
-							None,
-							Some(ScheduledV2 {
-								maybe_id: Some(b"test".to_vec()),
-								priority: 123,
-								call: Call::Logger(logger::Call::log(69, 1000)),
-								maybe_periodic: Some((456u64, 10)),
-								origin: system::RawOrigin::None.into(),
-								_phantom: PhantomData::<u64>::default(),
-							}),
-						]
-					),
-					(
-						1,
-						vec![
-							Some(ScheduledV2 {
-								maybe_id: None,
-								priority: 11,
-								call: Call::Logger(logger::Call::log(96, 100)),
-								maybe_periodic: None,
-								origin: system::RawOrigin::Root.into(),
-								_phantom: PhantomData::<u64>::default(),
-							}),
-							None,
-							Some(ScheduledV2 {
-								maybe_id: Some(b"test".to_vec()),
-								priority: 123,
-								call: Call::Logger(logger::Call::log(69, 1000)),
-								maybe_periodic: Some((456u64, 10)),
-								origin: system::RawOrigin::None.into(),
-								_phantom: PhantomData::<u64>::default(),
-							}),
-						]
-					),
-					(
-						2,
-						vec![
-							Some(ScheduledV2 {
-								maybe_id: None,
-								priority: 12,
-								call: Call::Logger(logger::Call::log(96, 100)),
-								maybe_periodic: None,
-								origin: system::RawOrigin::Root.into(),
-								_phantom: PhantomData::<u64>::default(),
-							}),
-							None,
-							Some(ScheduledV2 {
-								maybe_id: Some(b"test".to_vec()),
-								priority: 123,
-								call: Call::Logger(logger::Call::log(69, 1000)),
-								maybe_periodic: Some((456u64, 10)),
-								origin: system::RawOrigin::None.into(),
-								_phantom: PhantomData::<u64>::default(),
-							}),
-						]
-					)
-				]
-			);
-		});
-	}
 }
modifiedprimitives/nft/src/lib.rsdiffbeforeafterboth
--- a/primitives/nft/src/lib.rs
+++ b/primitives/nft/src/lib.rs
@@ -144,6 +144,7 @@
 	pub limits: CollectionLimits<T::BlockNumber>, // Collection private restrictions
 	pub variable_on_chain_schema: Vec<u8>,        //
 	pub const_on_chain_schema: Vec<u8>,           //
+	pub meta_update_permission: MetaUpdatePermission,
 }
 
 #[derive(Encode, Decode, Debug, Clone, PartialEq)]
@@ -241,6 +242,20 @@
 
 #[derive(Encode, Decode, Debug, Clone, PartialEq)]
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub enum MetaUpdatePermission  {
+	ItemOwner,
+	Admin,
+	None,
+}
+
+impl Default for MetaUpdatePermission {
+	fn default() -> Self {
+		Self::ItemOwner
+	}
+}
+
+#[derive(Encode, Decode, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 pub enum CreateItemData {
 	NFT(CreateNftData),
 	Fungible(CreateFungibleData),