git.delta.rocks / unique-network / refs/commits / eb5b53586bb9

difftreelog

Scheduler

str-mv2021-05-27parent: #172f80a.patch.diff
in: master

22 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3556,6 +3556,7 @@
  "futures 0.3.12",
  "jsonrpc-core",
  "log",
+ "nft-data-structs",
  "nft-runtime",
  "pallet-contracts-rpc",
  "pallet-transaction-payment-rpc",
@@ -3592,6 +3593,19 @@
 ]
 
 [[package]]
+name = "nft-data-structs"
+version = "0.9.0"
+dependencies = [
+ "frame-support",
+ "frame-system",
+ "pallet-contracts",
+ "parity-scale-codec",
+ "serde",
+ "sp-core",
+ "sp-runtime",
+]
+
+[[package]]
 name = "nft-runtime"
 version = "3.0.0"
 dependencies = [
@@ -3602,6 +3616,7 @@
  "frame-system-benchmarking",
  "frame-system-rpc-runtime-api",
  "hex-literal",
+ "nft-data-structs",
  "pallet-aura",
  "pallet-balances",
  "pallet-contracts",
@@ -3610,7 +3625,10 @@
  "pallet-grandpa",
  "pallet-inflation",
  "pallet-nft",
+ "pallet-nft-charge-transaction",
+ "pallet-nft-transaction-payment",
  "pallet-randomness-collective-flip",
+ "pallet-scheduler",
  "pallet-sudo",
  "pallet-timestamp",
  "pallet-transaction-payment",
@@ -3947,8 +3965,32 @@
  "frame-benchmarking",
  "frame-support",
  "frame-system",
+ "nft-data-structs",
+ "pallet-balances",
+ "pallet-contracts",
+ "pallet-randomness-collective-flip",
+ "pallet-timestamp",
+ "pallet-transaction-payment",
+ "parity-scale-codec",
+ "serde",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+]
+
+[[package]]
+name = "pallet-nft-charge-transaction"
+version = "3.0.0"
+dependencies = [
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
+ "nft-data-structs",
  "pallet-balances",
  "pallet-contracts",
+ "pallet-nft",
+ "pallet-nft-transaction-payment",
  "pallet-randomness-collective-flip",
  "pallet-timestamp",
  "pallet-transaction-payment",
@@ -3961,6 +4003,28 @@
 ]
 
 [[package]]
+name = "pallet-nft-transaction-payment"
+version = "3.0.0"
+dependencies = [
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
+ "nft-data-structs",
+ "pallet-balances",
+ "pallet-contracts",
+ "pallet-nft",
+ "pallet-randomness-collective-flip",
+ "pallet-timestamp",
+ "pallet-transaction-payment",
+ "parity-scale-codec",
+ "serde",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+]
+
+[[package]]
 name = "pallet-randomness-collective-flip"
 version = "3.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3975,6 +4039,27 @@
 ]
 
 [[package]]
+name = "pallet-scheduler"
+version = "3.0.0"
+dependencies = [
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
+ "log",
+ "nft-data-structs",
+ "pallet-contracts",
+ "pallet-nft",
+ "pallet-nft-transaction-payment",
+ "parity-scale-codec",
+ "serde",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+ "substrate-test-utils",
+]
+
+[[package]]
 name = "pallet-session"
 version = "3.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -7316,6 +7401,28 @@
 ]
 
 [[package]]
+name = "substrate-test-utils"
+version = "3.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f46809281d923956cdfdb2f95e07fa2437884f2a18096c4371a3fa48f8d082a"
+dependencies = [
+ "futures 0.3.12",
+ "substrate-test-utils-derive",
+ "tokio 0.2.25",
+]
+
+[[package]]
+name = "substrate-test-utils-derive"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "988016ca49cfc763fecf97dfa9dee7dbbff1a2145b003d07681278dddefd1a0e"
+dependencies = [
+ "proc-macro-crate",
+ "quote",
+ "syn",
+]
+
+[[package]]
 name = "substrate-wasm-builder"
 version = "4.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -7550,6 +7657,7 @@
  "pin-project-lite 0.1.11",
  "signal-hook-registry",
  "slab",
+ "tokio-macros",
  "winapi 0.3.9",
 ]
 
@@ -7618,6 +7726,17 @@
 ]
 
 [[package]]
+name = "tokio-macros"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
 name = "tokio-named-pipes"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
modifiedCargo.tomldiffbeforeafterboth
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,6 +2,8 @@
 members = [
     'node',
     'pallets/nft',
+    'pallets/nft-transaction-payment',
+    'primitives',
     'runtime',
 ]
 [profile.release]
modifiednode/Cargo.tomldiffbeforeafterboth
--- a/node/Cargo.toml
+++ b/node/Cargo.toml
@@ -28,6 +28,7 @@
 
 # Substrate dependencies
 nft-runtime = { path = '../runtime', version = '3.0.0' }
+nft-data-structs = { default-features = false, path="../primitives" }
 frame-benchmarking = '3.0.0'
 frame-benchmarking-cli = '3.0.0'
 pallet-transaction-payment-rpc = '3.0.0'
modifiednode/src/chain_spec.rsdiffbeforeafterboth
--- a/node/src/chain_spec.rs
+++ b/node/src/chain_spec.rs
@@ -3,19 +3,14 @@
 // file 'LICENSE', which is part of this source code package.
 //
 
-// use nft_runtime::{
-//     AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig, Signature, SudoConfig,
-//     SystemConfig, WASM_BINARY,
-// };
-// use nft_runtime::{ContractsConfig, ContractsSchedule, NftConfig, CollectionType};
 use nft_runtime::*;
+use nft_data_structs::*;
 use sc_service::ChainType;
 use sp_consensus_aura::sr25519::AuthorityId as AuraId;
 use sp_core::{sr25519, Pair, Public};
 use sp_finality_grandpa::AuthorityId as GrandpaId;
 use sp_runtime::traits::{IdentifyAccount, Verify};
 use serde_json::map::Map;
-// use crate::chain_spec::api::chain_extension::*;
 
 // Note this is the URL for the telemetry server
 //const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";
@@ -158,7 +153,7 @@
             balances: endowed_accounts
                 .iter()
                 .cloned()
-                .map(|k| (k, 1 << 100))
+                .map(|k| (k, 1 << 70))
                 .collect(),
         }),
         pallet_aura: Some(AuraConfig {
addedpallets/nft-charge-transaction/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/pallets/nft-charge-transaction/Cargo.toml
@@ -0,0 +1,59 @@
+[package]
+authors = ['Substrate DevHub <https://github.com/substrate-developer-hub>']
+description = 'Unqiue pallet nft specific transaction payment'
+edition = '2018'
+homepage = 'https://substrate.io'
+license = 'Unlicense'
+name = 'pallet-nft-charge-transaction'
+repository = 'https://github.com/usetech-llc/nft_private/'
+version = '3.0.0'
+
+[package.metadata.docs.rs]
+targets = ['x86_64-unknown-linux-gnu']
+
+# alias "parity-scale-code" to "codec"
+[dependencies.codec]
+default-features = false
+features = ['derive']
+package = 'parity-scale-codec'
+version = '2.0.0'
+
+[dependencies]
+serde = { version = "1.0.119" }
+frame-support = { default-features = false, version = '3.0.0' }
+frame-system = { default-features = false, version = '3.0.0' }
+pallet-balances = { default-features = false, version = '3.0.0' }
+pallet-contracts = { default-features = false, version = '3.0.0' }
+pallet-timestamp = { default-features = false, version = '3.0.0' }
+pallet-transaction-payment = { default-features = false, version = "3.0.0" }
+pallet-randomness-collective-flip = { default-features = false, version = '3.0.0' }
+sp-std = { default-features = false, version = '3.0.0' }
+frame-benchmarking = { default-features = false, version = "3.0.0", optional = true }
+sp-core = { default-features = false, version = '3.0.0' }
+sp-io = { default-features = false, version = '3.0.0' }
+sp-runtime = { default-features = false, version = '3.0.0' }
+
+pallet-nft = { default-features = false, path="../nft" }
+pallet-nft-transaction-payment = { default-features = false, path="../nft-transaction-payment" }
+nft-data-structs = { default-features = false, path="../../primitives" }
+
+[features]
+default = ['std']
+std = [
+    'codec/std',
+    'serde/std',
+    'frame-support/std',
+    'frame-system/std',
+    'pallet-balances/std',
+    'pallet-timestamp/std',
+    'pallet-randomness-collective-flip/std',
+    'pallet-contracts/std',
+    'pallet-nft/std',
+    'pallet-transaction-payment/std',
+    'pallet-nft-transaction-payment/std',
+    'sp-std/std',
+    'sp-runtime/std',
+    'nft-data-structs/std',
+    'frame-benchmarking/std',
+]
+runtime-benchmarks = ["frame-benchmarking"]
addedpallets/nft-charge-transaction/src/lib.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/nft-charge-transaction/src/lib.rs
@@ -0,0 +1,215 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+#[cfg(feature = "std")]
+pub use std::*;
+
+#[cfg(feature = "std")]
+pub use serde::*;
+
+#[cfg(feature = "runtime-benchmarks")]
+mod benchmarking;
+
+#[cfg(test)]
+mod tests;
+
+use codec::{Decode, Encode};
+use frame_support::traits::{ Get};
+use frame_support::{
+	decl_module, decl_storage,
+	traits::{
+		IsSubType, 
+	},
+	weights::{
+		DispatchInfo, PostDispatchInfo, DispatchClass
+	}
+};
+use sp_runtime::{
+	traits::{ 
+		DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,
+	},
+	transaction_validity::{ 
+		TransactionPriority, TransactionValidity, TransactionValidityError, ValidTransaction,
+	},
+	FixedPointOperand, DispatchResult
+};
+use pallet_contracts::chain_extension::UncheckedFrom;
+use pallet_transaction_payment::OnChargeTransaction;
+use sp_std::prelude::*;
+
+
+	pub trait Config: frame_system::Config + 
+		pallet_nft_transaction_payment::Config
+	{
+
+	}
+
+	decl_storage! {
+		trait Store for Module<T: Config> as NftTransactionPayment{
+
+	}
+}
+
+decl_module! {
+
+	pub struct Module<T: Config> for enum Call 
+    where 
+		origin: T::Origin,
+    {}
+}
+
+type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;
+
+/// Require the transactor pay for themselves and maybe include a tip to gain additional priority
+/// in the queue.
+#[derive(Encode, Decode, Clone, Eq, PartialEq)]
+pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);
+
+impl<T: Config + Send + Sync> sp_std::fmt::Debug 
+    for ChargeTransactionPayment<T>
+{
+	#[cfg(feature = "std")]
+	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
+		write!(f, "ChargeTransactionPayment<{:?}>", self.0)
+	}
+	#[cfg(not(feature = "std"))]
+	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
+		Ok(())
+	}
+}
+
+impl<T: Config> ChargeTransactionPayment<T>
+where
+	T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<pallet_nft::Call<T>> + IsSubType<pallet_contracts::Call<T>>,
+    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,
+    T::AccountId: AsRef<[u8]>,
+    T::AccountId: UncheckedFrom<T::Hash>,
+{
+    fn traditional_fee(
+        len: usize,
+        info: &DispatchInfoOf<T::Call>,
+        tip: BalanceOf<T>,
+    ) -> BalanceOf<T>
+    where
+        T::Call: Dispatchable<Info = DispatchInfo>,
+    {
+        <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)
+    }
+
+	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {
+        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);
+        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);
+        let len_saturation = max_block_length as u64 / (len as u64).max(1);
+        let coefficient: BalanceOf<T> = weight_saturation
+            .min(len_saturation)
+            .saturated_into::<BalanceOf<T>>();
+        final_fee
+            .saturating_mul(coefficient)
+            .saturated_into::<TransactionPriority>()
+    }
+
+    fn withdraw_fee(
+        &self,
+        who: &T::AccountId,
+        call: &T::Call,
+        info: &DispatchInfoOf<T::Call>,
+        len: usize,
+	) -> Result<
+		(
+			BalanceOf<T>,
+			<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,
+		),
+		TransactionValidityError,
+	> {
+        let tip = self.0;
+
+        let fee = Self::traditional_fee(len, info, tip);
+
+        // Only mess with balances if fee is not zero.
+        if fee.is_zero() {
+            return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)
+			.map(|i| (fee, i));
+        }
+
+        // Determine who is paying transaction fee based on ecnomic model
+		// Parse call to extract collection ID and access collection sponsor	
+		let sponsor = <pallet_nft_transaction_payment::Module<T>>::withdraw_type(who, call);
+
+        let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());
+
+		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)
+			.map(|i| (fee, i))
+    }
+}
+
+impl<T: Config + Send + Sync> SignedExtension
+    for ChargeTransactionPayment<T>
+where
+    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,
+	T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<pallet_nft::Call<T>> + IsSubType<pallet_contracts::Call<T>>,
+    T::AccountId: AsRef<[u8]>,
+    T::AccountId: UncheckedFrom<T::Hash>,
+{
+    const IDENTIFIER: &'static str = "ChargeTransactionPayment";
+    type AccountId = T::AccountId;
+    type Call = T::Call;
+    type AdditionalSigned = ();
+    type Pre = (
+        // tip
+        BalanceOf<T>,
+        // who pays fee
+        Self::AccountId,
+		// imbalance resulting from withdrawing the fee
+		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,
+    );
+    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {
+        Ok(())
+    }
+
+    fn validate(
+        &self,
+        who: &Self::AccountId,
+        call: &Self::Call,
+        info: &DispatchInfoOf<Self::Call>,
+        len: usize,
+    ) -> TransactionValidity {
+		let (fee, _) = self.withdraw_fee(who, call, info, len)?;
+		Ok(ValidTransaction {
+			priority: Self::get_priority(len, info, fee),
+			..Default::default()
+		})
+    }
+
+    fn pre_dispatch(
+        self,
+        who: &Self::AccountId,
+        call: &Self::Call,
+        info: &DispatchInfoOf<Self::Call>,
+        len: usize,
+    ) -> Result<Self::Pre, TransactionValidityError> {
+        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;
+        Ok((self.0, who.clone(), imbalance))
+    }
+
+    fn post_dispatch(
+        pre: Self::Pre,
+        info: &DispatchInfoOf<Self::Call>,
+        post_info: &PostDispatchInfoOf<Self::Call>,
+        len: usize,
+        _result: &DispatchResult,
+    ) -> Result<(), TransactionValidityError> {
+		let (tip, who, imbalance) = pre;
+		let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(
+			len as u32,
+			info,
+			post_info,
+			tip,
+		);
+		<T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;
+		Ok(())
+    }
+}
\ No newline at end of file
addedpallets/nft-transaction-payment/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/pallets/nft-transaction-payment/Cargo.toml
@@ -0,0 +1,57 @@
+[package]
+authors = ['Substrate DevHub <https://github.com/substrate-developer-hub>']
+description = 'Unqiue pallet nft specific transaction payment'
+edition = '2018'
+homepage = 'https://substrate.io'
+license = 'Unlicense'
+name = 'pallet-nft-transaction-payment'
+repository = 'https://github.com/usetech-llc/nft_private/'
+version = '3.0.0'
+
+[package.metadata.docs.rs]
+targets = ['x86_64-unknown-linux-gnu']
+
+# alias "parity-scale-code" to "codec"
+[dependencies.codec]
+default-features = false
+features = ['derive']
+package = 'parity-scale-codec'
+version = '2.0.0'
+
+[dependencies]
+serde = { version = "1.0.119" }
+frame-support = { default-features = false, version = '3.0.0' }
+frame-system = { default-features = false, version = '3.0.0' }
+pallet-balances = { default-features = false, version = '3.0.0' }
+pallet-contracts = { default-features = false, version = '3.0.0' }
+pallet-timestamp = { default-features = false, version = '3.0.0' }
+pallet-transaction-payment = { default-features = false, version = "3.0.0" }
+pallet-randomness-collective-flip = { default-features = false, version = '3.0.0' }
+sp-std = { default-features = false, version = '3.0.0' }
+frame-benchmarking = { default-features = false, version = "3.0.0", optional = true }
+sp-core = { default-features = false, version = '3.0.0' }
+sp-io = { default-features = false, version = '3.0.0' }
+sp-runtime = { default-features = false, version = '3.0.0' }
+
+pallet-nft = { default-features = false, path="../nft" }
+nft-data-structs = { default-features = false, path="../../primitives" }
+
+[features]
+default = ['std']
+std = [
+    'codec/std',
+    'serde/std',
+    'frame-support/std',
+    'frame-system/std',
+    'pallet-balances/std',
+    'pallet-timestamp/std',
+    'pallet-randomness-collective-flip/std',
+    'pallet-contracts/std',
+    'pallet-nft/std',
+    'pallet-transaction-payment/std',
+    'sp-std/std',
+    'sp-runtime/std',
+    'nft-data-structs/std',
+    'frame-benchmarking/std',
+]
+runtime-benchmarks = ["frame-benchmarking"]
addedpallets/nft-transaction-payment/src/benchmarking.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/nft-transaction-payment/src/benchmarking.rs
@@ -0,0 +1,19 @@
+#![cfg(feature = "runtime-benchmarks")]
+
+use super::*;
+use crate::Module as NftTransactionPayment;
+
+use sp_std::prelude::*;
+use frame_system::RawOrigin;
+use frame_benchmarking::{benchmarks}; 
+use frame_support::traits::OnInitialize;
+
+benchmarks! {
+
+	// on_initialize {
+        // let block1: T::BlockNumber = T::BlockNumber::from(1u32);
+        // let block2: T::BlockNumber = T::BlockNumber::from(2u32);
+        // Inflation::<T>::on_initialize(block1); // Create Treasury account
+	// }: { Inflation::<T>::on_initialize(block2); } // Benchmark deposit_into_existing path
+
+}
addedpallets/nft-transaction-payment/src/lib.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/nft-transaction-payment/src/lib.rs
@@ -0,0 +1,525 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+#[cfg(feature = "std")]
+pub use std::*;
+
+#[cfg(feature = "std")]
+pub use serde::*;
+
+#[cfg(feature = "runtime-benchmarks")]
+mod benchmarking;
+
+#[cfg(test)]
+mod tests;
+
+use frame_support::{
+	decl_error, decl_module, decl_storage,
+	traits::{
+		IsSubType, 
+	},
+	weights::{
+		DispatchInfo
+	}
+};
+use sp_runtime::traits::StaticLookup;
+use sp_runtime::{
+	traits::{ 
+		Hash, Dispatchable,
+	},
+};
+use pallet_contracts::chain_extension::UncheckedFrom;
+use sp_std::prelude::*;
+use nft_data_structs::{
+	CreateItemData,
+	CollectionId, CollectionMode, TokenId
+};
+
+type CodeHash<T> = <T as frame_system::Config>::Hash;
+
+	pub trait Config: frame_system::Config + pallet_contracts::Config + pallet_transaction_payment::Config + pallet_nft::Config {
+	}
+
+	// Error for non-fungible-token module.
+	
+	decl_error! {
+		/// Error for non-fungible-token module.
+		pub enum Error for Module<T: Config> {
+		/// No available class ID
+		NoAvailableClassId,
+		/// No available token ID
+		NoAvailableTokenId,
+		/// Token(ClassId, TokenId) not found
+		TokenNotFound,
+		/// Class not found
+		CollectionNotFound,
+		/// The operator is not the owner of the token and has no permission
+		NoPermission,
+		/// Arithmetic calculation overflow
+		NumOverflow,
+		/// Can not destroy class
+		/// Total issuance is not 0
+		CannotDestroyClass,
+	}
+}
+
+	decl_storage! {
+		trait Store for Module<T: Config> as NftTransactionPayment{
+
+	}
+}
+
+decl_module! {
+
+	pub struct Module<T: Config> for enum Call 
+    where 
+		origin: T::Origin,
+    {
+	}
+}
+
+impl<T: Config> Module<T> 
+{
+
+	pub fn withdraw_type(
+		who: &T::AccountId,
+		call: &T::Call
+	) -> Option<T::AccountId> where 
+		T::Call: Dispatchable<Info=DispatchInfo>,
+		T::Call: IsSubType<pallet_nft::Call<T>>, 
+		T::Call: IsSubType<pallet_contracts::Call<T>>,
+		T::AccountId: AsRef<[u8]>,
+		T::AccountId: UncheckedFrom<T::Hash>
+		{
+
+        let mut sponsor: Option<T::AccountId> = match IsSubType::<pallet_nft::Call<T>>::is_sub_type(call)  {
+            Some(pallet_nft::Call::create_item(collection_id, _owner, _properties)) => {
+
+                Self::withdraw_create_item(who, collection_id, &_properties)
+            },
+            Some(pallet_nft::Call::transfer(_new_owner, collection_id, item_id, _value)) => {
+
+                Self::withdraw_transfer(who, collection_id, item_id)
+            },
+            Some(pallet_nft::Call::set_variable_meta_data(collection_id, item_id, data)) => {
+
+                Self::withdraw_set_variable_meta_data(who, collection_id, item_id, &data)
+			},
+			_ => None,
+        };
+
+        sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {
+            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {
+
+                Self::withdraw_contract_call(who, dest)
+            },
+            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {
+
+                Self::withdraw_contract_instantiate(&who, code_hash, salt)
+            },
+            Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt))  => {
+
+                Self::withdraw_contract_instantiate(&who, &T::Hashing::hash(&_code), _salt)
+			},
+			_ => None,
+        });
+
+		sponsor
+	}
+
+
+
+	pub fn withdraw_create_item(
+		who: &T::AccountId,
+		collection_id: &CollectionId,
+		_properties: &CreateItemData,
+	) -> Option<T::AccountId> {
+	
+		let collection = pallet_nft::CollectionById::<T>::get(collection_id)?;
+
+		// sponsor timeout
+		let block_number = <frame_system::Module<T>>::block_number() as T::BlockNumber;
+
+		let limit = collection.limits.sponsor_transfer_timeout;
+		if pallet_nft::CreateItemBasket::<T>::contains_key((collection_id, &who)) {
+			let last_tx_block = pallet_nft::CreateItemBasket::<T>::get((collection_id, &who));
+			let limit_time = last_tx_block + limit.into();
+			if block_number <= limit_time {
+				return None;
+			}
+		}
+		pallet_nft::CreateItemBasket::<T>::insert((collection_id, who.clone()), block_number);
+
+		// check free create limit
+		if collection.limits.sponsored_data_size >= (_properties.len() as u32) {
+			collection.sponsorship.sponsor()
+				.cloned()
+		} else {
+			None
+		}
+	}
+
+	pub fn withdraw_transfer(
+		who: &T::AccountId,
+		collection_id: &CollectionId,
+		item_id: &TokenId,
+	) -> Option<T::AccountId> {
+
+		let collection = pallet_nft::CollectionById::<T>::get(collection_id)?;
+		let limits = pallet_nft::ChainLimit::get();
+
+		let mut sponsor_transfer = false;
+		if collection.sponsorship.confirmed() {
+
+			let collection_limits = collection.limits.clone();
+			let collection_mode = collection.mode.clone();
+
+			// sponsor timeout
+			let block_number = <frame_system::Module<T>>::block_number() as T::BlockNumber;
+			sponsor_transfer = match collection_mode {
+				CollectionMode::NFT => {
+
+					// get correct limit
+					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
+						collection_limits.sponsor_transfer_timeout
+					} else {
+						limits.nft_sponsor_transfer_timeout
+					};
+
+					let mut sponsored = true;
+					if pallet_nft::NftTransferBasket::<T>::contains_key(collection_id, item_id) {
+						let last_tx_block = pallet_nft::NftTransferBasket::<T>::get(collection_id, item_id);
+						let limit_time = last_tx_block + limit.into();
+						if block_number <= limit_time {
+							sponsored = false;
+						}
+					}
+					if sponsored {
+						pallet_nft::NftTransferBasket::<T>::insert(collection_id, item_id, block_number);
+					}
+
+					sponsored
+				}
+				CollectionMode::Fungible(_) => {
+
+					// get correct limit
+					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
+						collection_limits.sponsor_transfer_timeout
+					} else {
+						limits.fungible_sponsor_transfer_timeout
+					};
+
+					let block_number = <frame_system::Module<T>>::block_number() as T::BlockNumber;
+					let mut sponsored = true;
+					if pallet_nft::FungibleTransferBasket::<T>::contains_key(collection_id, who) {
+						let last_tx_block = pallet_nft::FungibleTransferBasket::<T>::get(collection_id, who);
+						let limit_time = last_tx_block + limit.into();
+						if block_number <= limit_time {
+							sponsored = false;
+						}
+					}
+					if sponsored {
+						pallet_nft::FungibleTransferBasket::<T>::insert(collection_id, who, block_number);
+					}
+
+					sponsored
+				}
+				CollectionMode::ReFungible => {
+
+					// get correct limit
+					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
+						collection_limits.sponsor_transfer_timeout
+					} else {
+						limits.refungible_sponsor_transfer_timeout
+					};
+
+					let mut sponsored = true;
+					if pallet_nft::ReFungibleTransferBasket::<T>::contains_key(collection_id, item_id) {
+						let last_tx_block = pallet_nft::ReFungibleTransferBasket::<T>::get(collection_id, item_id);
+						let limit_time = last_tx_block + limit.into();
+						if block_number <= limit_time {
+							sponsored = false;
+						}
+					}
+					if sponsored {
+						pallet_nft::ReFungibleTransferBasket::<T>::insert(collection_id, item_id, block_number);
+					}
+
+					sponsored
+				}
+				_ => {
+					false
+				},
+			};
+		}
+
+		if !sponsor_transfer {
+			None
+		} else {
+			collection.sponsorship.sponsor()
+				.cloned()
+		}
+	}
+	
+	pub fn withdraw_set_variable_meta_data(
+		who: &T::AccountId,
+		collection_id: &CollectionId,
+		item_id: &TokenId,
+		data: &Vec<u8>,
+	) -> Option<T::AccountId> {
+
+		let mut sponsor_metadata_changes = false;
+
+		let collection = pallet_nft::CollectionById::<T>::get(collection_id)?;
+
+		if
+			collection.sponsorship.confirmed() &&
+			// Can't sponsor fungible collection, this tx will be rejected
+			// as invalid
+			!matches!(collection.mode, CollectionMode::Fungible(_)) &&
+			data.len() <= collection.limits.sponsored_data_size as usize
+		{
+			if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {
+				let block_number = <frame_system::Module<T>>::block_number() as T::BlockNumber;
+
+				if pallet_nft::VariableMetaDataBasket::<T>::get(collection_id, item_id)
+					.map(|last_block| block_number - last_block > rate_limit)
+					.unwrap_or(true) 
+				{
+					sponsor_metadata_changes = true;
+					pallet_nft::VariableMetaDataBasket::<T>::insert(collection_id, item_id, block_number);
+				}
+			}
+		}
+
+		if !sponsor_metadata_changes {
+			None
+		} else {
+			collection.sponsorship.sponsor().cloned()
+		}
+
+	}
+
+	pub fn withdraw_contract_call(
+		who: &T::AccountId,
+		dest: &<T::Lookup as StaticLookup>::Source
+	) -> Option<T::AccountId> {
+
+		let called_contract: T::AccountId = T::Lookup::lookup((dest).clone()).unwrap_or(T::AccountId::default());
+
+		let owned_contract = pallet_nft::ContractOwner::<T>::get(called_contract.clone()).as_ref() == Some(who);
+		let white_list_enabled = pallet_nft::ContractWhiteListEnabled::<T>::contains_key(called_contract.clone());
+		  
+		// ???
+		if !owned_contract && white_list_enabled {
+		 	if !pallet_nft::ContractWhiteList::<T>::contains_key(called_contract.clone(), who) {
+				return Some(who.clone())
+		 		// return Err(InvalidTransaction::Call.into());
+		 	}
+		}
+
+		let mut sponsor_transfer = false;
+		if pallet_nft::ContractSponsoringRateLimit::<T>::contains_key(called_contract.clone()) {
+			let last_tx_block = pallet_nft::ContractSponsorBasket::<T>::get((&called_contract, &who));
+			let block_number = <frame_system::Module<T>>::block_number() as T::BlockNumber;
+			let rate_limit = pallet_nft::ContractSponsoringRateLimit::<T>::get(&called_contract);
+			let limit_time = last_tx_block + rate_limit;
+
+			if block_number >= limit_time {
+				pallet_nft::ContractSponsorBasket::<T>::insert((called_contract.clone(), who.clone()), block_number);
+				sponsor_transfer = true;
+			}
+		} else {
+			sponsor_transfer = false;
+		}
+	   
+		if sponsor_transfer {
+			if pallet_nft::ContractSelfSponsoring::<T>::contains_key(called_contract.clone()) {
+				if pallet_nft::ContractSelfSponsoring::<T>::get(called_contract.clone()) {
+					return Some(called_contract);
+				}
+			}
+		}
+
+		None
+	}
+
+	pub fn withdraw_contract_instantiate(
+		who: &T::AccountId,
+		code_hash: &CodeHash<T>,
+		salt: &[u8],
+	) -> Option<T::AccountId> where
+	T::AccountId: AsRef<[u8]>,
+	T::AccountId: UncheckedFrom<T::Hash>
+	{
+
+		let new_contract_address = <pallet_contracts::Module<T>>::contract_address(
+			&who,
+			code_hash,
+			salt,
+		);
+		pallet_nft::ContractOwner::<T>::insert(new_contract_address.clone(), who.clone());
+
+		None
+	}
+}
+
+// type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;
+
+// /// Require the transactor pay for themselves and maybe include a tip to gain additional priority
+// /// in the queue.
+// #[derive(Encode, Decode, Clone, Eq, PartialEq)]
+// pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);
+
+// impl<T: Config + Send + Sync> sp_std::fmt::Debug 
+//     for ChargeTransactionPayment<T>
+// {
+// 	#[cfg(feature = "std")]
+// 	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
+// 		write!(f, "ChargeTransactionPayment<{:?}>", self.0)
+// 	}
+// 	#[cfg(not(feature = "std"))]
+// 	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
+// 		Ok(())
+// 	}
+// }
+
+// impl<T: Config> ChargeTransactionPayment<T>
+// where
+// 	T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<pallet_nft::Call<T>> + IsSubType<pallet_contracts::Call<T>>,
+//     BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,
+//     T::AccountId: AsRef<[u8]>,
+//     T::AccountId: UncheckedFrom<T::Hash>,
+// {
+//     fn traditional_fee(
+//         len: usize,
+//         info: &DispatchInfoOf<T::Call>,
+//         tip: BalanceOf<T>,
+//     ) -> BalanceOf<T>
+//     where
+//         T::Call: Dispatchable<Info = DispatchInfo>,
+//     {
+//         <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)
+//     }
+
+// 	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {
+//         let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);
+//         let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);
+//         let len_saturation = max_block_length as u64 / (len as u64).max(1);
+//         let coefficient: BalanceOf<T> = weight_saturation
+//             .min(len_saturation)
+//             .saturated_into::<BalanceOf<T>>();
+//         final_fee
+//             .saturating_mul(coefficient)
+//             .saturated_into::<TransactionPriority>()
+//     }
+
+//     fn withdraw_fee(
+//         &self,
+//         who: &T::AccountId,
+//         call: &T::Call,
+//         info: &DispatchInfoOf<T::Call>,
+//         len: usize,
+// 	) -> Result<
+// 		(
+// 			BalanceOf<T>,
+// 			<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,
+// 		),
+// 		TransactionValidityError,
+// 	> {
+//         let tip = self.0;
+
+//         let fee = Self::traditional_fee(len, info, tip);
+
+//         // Only mess with balances if fee is not zero.
+//         if fee.is_zero() {
+//             return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)
+// 			.map(|i| (fee, i));
+//         }
+
+//         // Determine who is paying transaction fee based on ecnomic model
+// 		// Parse call to extract collection ID and access collection sponsor
+		
+// 		let sponsor = pallet_nft_transaction_payment::<T>::withdraw_type(who, call);
+// 		//let sponsor = Self::Module::<T>::withdraw_type(who, call);;
+// 		// <pallet_nft_transaction_payment::Module<T>>::withdraw_type(who, call);
+
+//         let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());
+
+// 		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)
+// 			.map(|i| (fee, i))
+//     }
+// }
+
+
+// impl<T: Config + Send + Sync> SignedExtension
+//     for ChargeTransactionPayment<T>
+// where
+//     BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,
+// 	T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<pallet_nft::Call<T>> + IsSubType<pallet_contracts::Call<T>>,
+//     T::AccountId: AsRef<[u8]>,
+//     T::AccountId: UncheckedFrom<T::Hash>,
+// {
+//     const IDENTIFIER: &'static str = "ChargeTransactionPayment";
+//     type AccountId = T::AccountId;
+//     type Call = T::Call;
+//     type AdditionalSigned = ();
+//     type Pre = (
+//         // tip
+//         BalanceOf<T>,
+//         // who pays fee
+//         Self::AccountId,
+// 		// imbalance resulting from withdrawing the fee
+// 		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,
+//     );
+//     fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {
+//         Ok(())
+//     }
+
+//     fn validate(
+//         &self,
+//         who: &Self::AccountId,
+//         call: &Self::Call,
+//         info: &DispatchInfoOf<Self::Call>,
+//         len: usize,
+//     ) -> TransactionValidity {
+// 		let (fee, _) = self.withdraw_fee(who, call, info, len)?;
+// 		Ok(ValidTransaction {
+// 			priority: Self::get_priority(len, info, fee),
+// 			..Default::default()
+// 		})
+//     }
+
+//     fn pre_dispatch(
+//         self,
+//         who: &Self::AccountId,
+//         call: &Self::Call,
+//         info: &DispatchInfoOf<Self::Call>,
+//         len: usize,
+//     ) -> Result<Self::Pre, TransactionValidityError> {
+//         let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;
+//         Ok((self.0, who.clone(), imbalance))
+//     }
+
+//     fn post_dispatch(
+//         pre: Self::Pre,
+//         info: &DispatchInfoOf<Self::Call>,
+//         post_info: &PostDispatchInfoOf<Self::Call>,
+//         len: usize,
+//         _result: &DispatchResult,
+//     ) -> Result<(), TransactionValidityError> {
+// 		let (tip, who, imbalance) = pre;
+// 		let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(
+// 			len as u32,
+// 			info,
+// 			post_info,
+// 			tip,
+// 		);
+// 		<T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;
+// 		Ok(())
+//     }
+// }
\ No newline at end of file
addedpallets/nft-transaction-payment/src/tests.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/nft-transaction-payment/src/tests.rs
@@ -0,0 +1,241 @@
+#[cfg(test)]
+mod tests {
+	use crate as pallet_inflation;
+
+	use frame_system;
+	use frame_support::{traits::{Currency}, parameter_types};
+	use frame_support::{traits::OnInitialize};
+	use sp_core::H256;
+	use sp_runtime::{traits::{BlakeTwo256, IdentityLookup}, testing::Header};
+
+	type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
+	type Block = frame_system::mocking::MockBlock<Test>;
+
+	const YEAR: u64 = 5_259_600;
+
+	parameter_types! {
+		pub const ExistentialDeposit: u64 = 1;
+		pub const MaxLocks: u32 = 50;
+	}
+	
+	impl pallet_balances::Config for Test {
+		type AccountStore = System;
+		type Balance = u64;
+		type DustRemoval = ();
+		type Event = ();
+		type ExistentialDeposit = ExistentialDeposit;
+		type WeightInfo = ();
+		type MaxLocks = MaxLocks;
+	}
+	
+	frame_support::construct_runtime!(
+		pub enum Test where
+			Block = Block,
+			NodeBlock = Block,
+			UncheckedExtrinsic = UncheckedExtrinsic,
+		{
+			Balances: pallet_balances::{Module, Call, Storage},
+			System: frame_system::{Module, Call, Config, Storage, Event<T>},
+			Inflation: pallet_inflation::{Module, Call, Storage},
+		}
+	);
+
+	parameter_types! {
+		pub const BlockHashCount: u64 = 250;
+		pub BlockWeights: frame_system::limits::BlockWeights =
+			frame_system::limits::BlockWeights::simple_max(1024);
+		pub const SS58Prefix: u8 = 42;
+	}
+
+	impl frame_system::Config for Test {
+		type BaseCallFilter = ();
+		type BlockWeights = ();
+		type BlockLength = ();
+		type DbWeight = ();
+		type Origin = Origin;
+		type Call = Call;
+		type Index = u64;
+		type BlockNumber = u64;
+		type Hash = H256;
+		type Hashing = BlakeTwo256;
+		type AccountId = u64;
+		type Lookup = IdentityLookup<Self::AccountId>;
+		type Header = Header;
+		type Event = ();
+		type BlockHashCount = BlockHashCount;
+		type Version = ();
+		type PalletInfo = PalletInfo;
+		type AccountData = pallet_balances::AccountData<u64>;
+		type OnNewAccount = ();
+		type OnKilledAccount = ();
+		type SystemWeightInfo = ();
+		type SS58Prefix = SS58Prefix;
+	}
+
+	parameter_types! {
+		pub TreasuryAccountId: u64 = 1234;
+		pub const InflationBlockInterval: u32 = 100; // every time per how many blocks inflation is applied
+	}
+		
+	impl pallet_inflation::Config for Test {
+		type Currency = Balances;
+		type TreasuryAccountId = TreasuryAccountId;
+		type InflationBlockInterval = InflationBlockInterval;
+	}
+
+	// Build genesis storage according to the mock runtime.
+	pub fn new_test_ext() -> sp_io::TestExternalities {
+		frame_system::GenesisConfig::default().build_storage::<Test>().unwrap().into()
+	}
+
+	#[test]
+	fn inflation_works() {
+		new_test_ext().execute_with(|| {
+			// Total issuance = 1_000_000_000
+			let initial_issuance: u64 = 1_000_000_000;
+			let _ = <Balances as Currency<_>>::deposit_creating(&1234, initial_issuance);
+			assert_eq!(Balances::free_balance(1234), initial_issuance);
+
+			// BlockInflation should be set after 1st block and 
+			// first inflation deposit should be equal to BlockInflation
+			Inflation::on_initialize(1);
+			assert!(Inflation::block_inflation() > 0);
+			assert_eq!(Balances::free_balance(1234) - initial_issuance, Inflation::block_inflation());
+		});
+	}
+
+	#[test]
+	fn inflation_second_deposit() {
+		new_test_ext().execute_with(|| {
+			// Total issuance = 1_000_000_000
+			let initial_issuance: u64 = 1_000_000_000;
+			let _ = <Balances as Currency<_>>::deposit_creating(&1234, initial_issuance);
+			assert_eq!(Balances::free_balance(1234), initial_issuance);
+			Inflation::on_initialize(1);
+
+			// Next inflation deposit happens when block is multiple of InflationBlockInterval
+			let mut block: u32 = 2;
+			let balance_before: u64 = Balances::free_balance(1234);
+			while block % InflationBlockInterval::get() != 0 {
+				Inflation::on_initialize(block as u64);
+				block += 1;
+			}
+			let balance_just_before: u64 = Balances::free_balance(1234);
+			assert_eq!(balance_before, balance_just_before);
+
+			// The block with inflation
+			Inflation::on_initialize(block as u64);
+			let balance_after: u64 = Balances::free_balance(1234);
+			assert_eq!(balance_after - balance_just_before, Inflation::block_inflation());
+		});
+	}
+
+	#[test]
+	fn inflation_in_1_year() {
+		new_test_ext().execute_with(|| {
+			// Total issuance = 1_000_000_000
+			let initial_issuance: u64 = 1_000_000_000;
+			let _ = <Balances as Currency<_>>::deposit_creating(&1234, initial_issuance);
+			assert_eq!(Balances::free_balance(1234), initial_issuance);
+			Inflation::on_initialize(1);
+			let block_inflation_year_0 = Inflation::block_inflation();
+
+			Inflation::on_initialize(YEAR);
+			let block_inflation_year_1 = Inflation::block_inflation();
+
+			// Assert that year 1 inflation is less than year 0
+			assert!(block_inflation_year_0 > block_inflation_year_1);
+		});
+	}
+
+	#[test]
+	fn inflation_in_1_to_9_years() {
+		new_test_ext().execute_with(|| {
+			// Total issuance = 1_000_000_000
+			let initial_issuance: u64 = 1_000_000_000;
+			let _ = <Balances as Currency<_>>::deposit_creating(&1234, initial_issuance);
+			assert_eq!(Balances::free_balance(1234), initial_issuance);
+			Inflation::on_initialize(1);
+
+			for year in 1..=9 {
+				let block_inflation_year_before = Inflation::block_inflation();
+				Inflation::on_initialize(YEAR * year);
+				let block_inflation_year_after = Inflation::block_inflation();
+
+				// Assert that next year inflation is less than previous year inflation
+				assert!(block_inflation_year_before > block_inflation_year_after);
+			}
+
+		});
+	}
+
+	#[test]
+	fn inflation_after_year_10_is_flat() {
+		new_test_ext().execute_with(|| {
+			// Total issuance = 1_000_000_000
+			let initial_issuance: u64 = 1_000_000_000;
+			let _ = <Balances as Currency<_>>::deposit_creating(&1234, initial_issuance);
+			assert_eq!(Balances::free_balance(1234), initial_issuance);
+			Inflation::on_initialize(YEAR * 9);
+
+			for year in 10..=20 {
+				let block_inflation_year_before = Inflation::block_inflation();
+				Inflation::on_initialize(YEAR * year);
+				let block_inflation_year_after = Inflation::block_inflation();
+
+				// Assert that next year inflation is equal to previous year inflation
+				assert_eq!(block_inflation_year_before, block_inflation_year_after);
+			}
+		});
+	}
+
+	#[test]
+	fn inflation_rate_by_year() {
+		new_test_ext().execute_with(|| {
+			let payouts: u64 = YEAR / InflationBlockInterval::get() as u64;
+
+			// Inflation starts at 10% and does down by 2/3% every year until year 9 (included), 
+			// then it is flat.
+			let payout_by_year: [u64; 11] = [
+				1000,
+				933,
+				867,
+				800,
+				733,
+				667,
+				600,
+				533,
+				467,
+				400,
+				400
+			];
+
+			// For accuracy total issuance = payout0 * payouts * 10;
+			let initial_issuance: u64 = payout_by_year[0] * payouts * 10;
+			let _ = <Balances as Currency<_>>::deposit_creating(&1234, initial_issuance);
+			assert_eq!(Balances::free_balance(1234), initial_issuance);
+
+			for year in 0..=10 {
+				// Year first block
+				Inflation::on_initialize(year*YEAR);
+				let mut actual_payout = Inflation::block_inflation();
+				assert_eq!(actual_payout, payout_by_year[year as usize]);
+
+				// Year second block
+				Inflation::on_initialize(year*YEAR+1);
+				actual_payout = Inflation::block_inflation();
+				assert_eq!(actual_payout, payout_by_year[year as usize]);
+
+				// Year middle block
+				Inflation::on_initialize(year*YEAR + YEAR/2);
+				actual_payout = Inflation::block_inflation();
+				assert_eq!(actual_payout, payout_by_year[year as usize]);
+
+				// Year last block
+				Inflation::on_initialize((year + 1)*YEAR-1);
+				actual_payout = Inflation::block_inflation();
+				assert_eq!(actual_payout, payout_by_year[year as usize]);
+			}
+		});
+	}
+}
modifiedpallets/nft/Cargo.tomldiffbeforeafterboth
--- a/pallets/nft/Cargo.toml
+++ b/pallets/nft/Cargo.toml
@@ -32,6 +32,8 @@
 sp-runtime = { default-features = false, version = '3.0.0' }
 pallet-contracts = { default-features = false, version = '3.0.0' }
 pallet-transaction-payment = { default-features = false, version = "3.0.0" }
+nft-data-structs = { default-features = false, path="../../primitives" }
+
 
 [features]
 default = ['std']
@@ -45,6 +47,7 @@
     'pallet-contracts/std',
     'pallet-randomness-collective-flip/std',
     'pallet-transaction-payment/std',
+    'nft-data-structs/std',
     'sp-std/std',
     'sp-runtime/std',
     'frame-benchmarking/std',
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
before · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use core::ops::{Deref, DerefMut};17use codec::{Decode, Encode};18pub use frame_support::{19    construct_runtime, decl_event, decl_module, decl_storage, decl_error,20    dispatch::DispatchResult,21    ensure, fail, parameter_types,22    traits::{23        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,24        Randomness, IsSubType, WithdrawReasons,25    },26    weights::{27        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},28        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,29        WeightToFeePolynomial, DispatchClass,30    },31    StorageValue,32    transactional,33};3435use frame_system::{self as system, ensure_signed, ensure_root};36use sp_runtime::sp_std::prelude::Vec;37use sp_runtime::{38    traits::{39        Hash, DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,40    },41    transaction_validity::{42        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,43    },44    FixedPointOperand, FixedU128,45};46use sp_runtime::traits::StaticLookup;47use pallet_contracts::chain_extension::UncheckedFrom;48use pallet_transaction_payment::OnChargeTransaction;4950#[cfg(test)]51mod mock;5253#[cfg(test)]54mod tests;5556mod default_weights;5758pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;59pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;60pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;61pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6263// Structs64// #region6566pub type CollectionId = u32;67pub type TokenId = u32;68pub type DecimalPoints = u8;6970#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]71#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]72pub enum CollectionMode {73    Invalid,74    NFT,75    // decimal points76    Fungible(DecimalPoints),77    ReFungible,78}7980impl Default for CollectionMode {81    fn default() -> Self {82        Self::Invalid83    }84}8586impl Into<u8> for CollectionMode {87    fn into(self) -> u8 {88        match self {89            CollectionMode::Invalid => 0,90            CollectionMode::NFT => 1,91            CollectionMode::Fungible(_) => 2,92            CollectionMode::ReFungible => 3,93        }94    }95}9697#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]98#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]99pub enum AccessMode {100    Normal,101    WhiteList,102}103impl Default for AccessMode {104    fn default() -> Self {105        Self::Normal106    }107}108109#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]110#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]111pub enum SchemaVersion {112    ImageURL,113    Unique,114}115impl Default for SchemaVersion {116    fn default() -> Self {117        Self::ImageURL118    }119}120121#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]122#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]123pub struct Ownership<AccountId> {124    pub owner: AccountId,125    pub fraction: u128,126}127128#[derive(Encode, Decode, Debug, Clone, PartialEq)]129#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]130pub enum SponsorshipState<AccountId> {131    /// The fees are applied to the transaction sender132    Disabled,133    Unconfirmed(AccountId),134    /// Transactions are sponsored by specified account135    Confirmed(AccountId),136}137138impl<AccountId> SponsorshipState<AccountId> {139    fn sponsor(&self) -> Option<&AccountId> {140        match self {141            Self::Confirmed(sponsor) => Some(sponsor),142            _ => None,143        }144    }145146    fn pending_sponsor(&self) -> Option<&AccountId> {147        match self {148            Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),149            _ => None,150        }151    }152153    fn confirmed(&self) -> bool {154        matches!(self, Self::Confirmed(_))155    }156}157158impl<T> Default for SponsorshipState<T> {159    fn default() -> Self {160        Self::Disabled161    }162}163164#[derive(Encode, Decode, Clone, PartialEq)]165#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]166pub struct Collection<T: Config> {167    pub owner: T::AccountId,168    pub mode: CollectionMode,169    pub access: AccessMode,170    pub decimal_points: DecimalPoints,171    pub name: Vec<u16>,        // 64 include null escape char172    pub description: Vec<u16>, // 256 include null escape char173    pub token_prefix: Vec<u8>, // 16 include null escape char174    pub mint_mode: bool,175    pub offchain_schema: Vec<u8>,176    pub schema_version: SchemaVersion,177    pub sponsorship: SponsorshipState<T::AccountId>,178    pub limits: CollectionLimits<T::BlockNumber>, // Collection private restrictions 179    pub variable_on_chain_schema: Vec<u8>, //180    pub const_on_chain_schema: Vec<u8>, //181}182183pub struct CollectionHandle<T: Config> {184    pub id: CollectionId,185    collection: Collection<T>,186}187188impl<T: Config> Deref for CollectionHandle<T> {189    type Target = Collection<T>;190191    fn deref(&self) -> &Self::Target {192        &self.collection193    }194}195196impl<T: Config> DerefMut for CollectionHandle<T> {197    fn deref_mut(&mut self) -> &mut Self::Target {198        &mut self.collection199    }200}201202#[derive(Encode, Decode, Debug, Clone, PartialEq)]203#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]204pub struct NftItemType<AccountId> {205    pub owner: AccountId,206    pub const_data: Vec<u8>,207    pub variable_data: Vec<u8>,208}209210#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]211#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]212pub struct FungibleItemType {213    pub value: u128,214}215216#[derive(Encode, Decode, Debug, Clone, PartialEq)]217#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]218pub struct ReFungibleItemType<AccountId> {219    pub owner: Vec<Ownership<AccountId>>,220    pub const_data: Vec<u8>,221    pub variable_data: Vec<u8>,222}223224// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]225// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]226// pub struct VestingItem<AccountId, Moment> {227//     pub sender: AccountId,228//     pub recipient: AccountId,229//     pub collection_id: CollectionId,230//     pub item_id: TokenId,231//     pub amount: u64,232//     pub vesting_date: Moment,233// }234235#[derive(Encode, Decode, Debug, Clone, PartialEq)]236#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]237pub struct CollectionLimits<BlockNumber: Encode + Decode> {238    pub account_token_ownership_limit: u32,239    pub sponsored_data_size: u32,240    /// None - setVariableMetadata is not sponsored241    /// Some(v) - setVariableMetadata is sponsored 242    ///           if there is v block between txs243    pub sponsored_data_rate_limit: Option<BlockNumber>,244    pub token_limit: u32,245246    // Timeouts for item types in passed blocks247    pub sponsor_transfer_timeout: u32,248    pub owner_can_transfer: bool,249    pub owner_can_destroy: bool,250}251252impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {253    fn default() -> Self {254        Self { 255            account_token_ownership_limit: 10_000_000, 256            token_limit: u32::max_value(),257            sponsored_data_size: u32::MAX, 258            sponsored_data_rate_limit: None,259            sponsor_transfer_timeout: 14400,260            owner_can_transfer: true,261            owner_can_destroy: true262        }263    }264}265266#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]267#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]268pub struct ChainLimits {269    pub collection_numbers_limit: u32,270    pub account_token_ownership_limit: u32,271    pub collections_admins_limit: u64,272    pub custom_data_limit: u32,273274    // Timeouts for item types in passed blocks275    pub nft_sponsor_transfer_timeout: u32,276    pub fungible_sponsor_transfer_timeout: u32,277    pub refungible_sponsor_transfer_timeout: u32,278279    // Schema limits280    pub offchain_schema_limit: u32,281    pub variable_on_chain_schema_limit: u32,282    pub const_on_chain_schema_limit: u32,283}284285pub trait WeightInfo {286	fn create_collection() -> Weight;287	fn destroy_collection() -> Weight;288	fn add_to_white_list() -> Weight;289	fn remove_from_white_list() -> Weight;290    fn set_public_access_mode() -> Weight;291    fn set_mint_permission() -> Weight;292    fn change_collection_owner() -> Weight;293    fn add_collection_admin() -> Weight;294    fn remove_collection_admin() -> Weight;295    fn set_collection_sponsor() -> Weight;296    fn confirm_sponsorship() -> Weight;297    fn remove_collection_sponsor() -> Weight;298    fn create_item(s: usize) -> Weight;299    fn burn_item() -> Weight;300    fn transfer() -> Weight;301    fn approve() -> Weight;302    fn transfer_from() -> Weight;303    fn set_offchain_schema() -> Weight;304    fn set_const_on_chain_schema() -> Weight;305    fn set_variable_on_chain_schema() -> Weight;306    fn set_variable_meta_data() -> Weight;307    fn enable_contract_sponsoring() -> Weight;308    fn set_schema_version() -> Weight;309    fn set_chain_limits() -> Weight;310    fn set_contract_sponsoring_rate_limit() -> Weight;311    fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;312    fn toggle_contract_white_list() -> Weight;313    fn add_to_contract_white_list() -> Weight;314    fn remove_from_contract_white_list() -> Weight;315    fn set_collection_limits() -> Weight;316}317318#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]319#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]320pub struct CreateNftData {321    pub const_data: Vec<u8>,322    pub variable_data: Vec<u8>,323}324325#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]326#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]327pub struct CreateFungibleData {328    pub value: u128,329}330331#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]332#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]333pub struct CreateReFungibleData {334    pub const_data: Vec<u8>,335    pub variable_data: Vec<u8>,336    pub pieces: u128,337}338339#[derive(Encode, Decode, Debug, Clone, PartialEq)]340#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]341pub enum CreateItemData {342    NFT(CreateNftData),343    Fungible(CreateFungibleData),344    ReFungible(CreateReFungibleData),345}346347impl CreateItemData {348    pub fn len(&self) -> usize {349        let len = match self {350            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),351            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),352            _ => 0353        };354        355        return len;356    }357}358359impl From<CreateNftData> for CreateItemData {360    fn from(item: CreateNftData) -> Self {361        CreateItemData::NFT(item)362    }363}364365impl From<CreateReFungibleData> for CreateItemData {366    fn from(item: CreateReFungibleData) -> Self {367        CreateItemData::ReFungible(item)368    }369}370371impl From<CreateFungibleData> for CreateItemData {372    fn from(item: CreateFungibleData) -> Self {373        CreateItemData::Fungible(item)374    }375}376377378decl_error! {379	/// Error for non-fungible-token module.380	pub enum Error for Module<T: Config> {381        /// Total collections bound exceeded.382        TotalCollectionsLimitExceeded,383		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.384        CollectionDecimalPointLimitExceeded, 385        /// Collection name can not be longer than 63 char.386        CollectionNameLimitExceeded, 387        /// Collection description can not be longer than 255 char.388        CollectionDescriptionLimitExceeded, 389        /// Token prefix can not be longer than 15 char.390        CollectionTokenPrefixLimitExceeded,391        /// This collection does not exist.392        CollectionNotFound,393        /// Item not exists.394        TokenNotFound,395        /// Admin not found396        AdminNotFound,397        /// Arithmetic calculation overflow.398        NumOverflow,       399        /// Account already has admin role.400        AlreadyAdmin,  401        /// You do not own this collection.402        NoPermission,403        /// This address is not set as sponsor, use setCollectionSponsor first.404        ConfirmUnsetSponsorFail,405        /// Collection is not in mint mode.406        PublicMintingNotAllowed,407        /// Sender parameter and item owner must be equal.408        MustBeTokenOwner,409        /// Item balance not enough.410        TokenValueTooLow,411        /// Size of item is too large.412        NftSizeLimitExceeded,413        /// No approve found414        ApproveNotFound,415        /// Requested value more than approved.416        TokenValueNotEnough,417        /// Only approved addresses can call this method.418        ApproveRequired,419        /// Address is not in white list.420        AddresNotInWhiteList,421        /// Number of collection admins bound exceeded.422        CollectionAdminsLimitExceeded,423        /// Owned tokens by a single address bound exceeded.424        AddressOwnershipLimitExceeded,425        /// Length of items properties must be greater than 0.426        EmptyArgument,427        /// const_data exceeded data limit.428        TokenConstDataLimitExceeded,429        /// variable_data exceeded data limit.430        TokenVariableDataLimitExceeded,431        /// Not NFT item data used to mint in NFT collection.432        NotNftDataUsedToMintNftCollectionToken,433        /// Not Fungible item data used to mint in Fungible collection.434        NotFungibleDataUsedToMintFungibleCollectionToken,435        /// Not Re Fungible item data used to mint in Re Fungible collection.436        NotReFungibleDataUsedToMintReFungibleCollectionToken,437        /// Unexpected collection type.438        UnexpectedCollectionType,439        /// Can't store metadata in fungible tokens.440        CantStoreMetadataInFungibleTokens,441        /// Collection token limit exceeded442        CollectionTokenLimitExceeded,443        /// Account token limit exceeded per collection444        AccountTokenLimitExceeded,445        /// Collection limit bounds per collection exceeded446        CollectionLimitBoundsExceeded,447        /// Tried to enable permissions which are only permitted to be disabled448        OwnerPermissionsCantBeReverted,449        /// Schema data size limit bound exceeded450        SchemaDataLimitExceeded,451        /// Maximum refungibility exceeded452        WrongRefungiblePieces,453        /// createRefungible should be called with one owner454        BadCreateRefungibleCall,455	}456}457458pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {459    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;460461    /// Weight information for extrinsics in this pallet.462	type WeightInfo: WeightInfo;463464    type Currency: Currency<Self::AccountId>;465    type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;466    type TreasuryAccountId: Get<Self::AccountId>;467}468469#[cfg(feature = "runtime-benchmarks")]470mod benchmarking;471472// #endregion473474// # Used definitions475//476// ## User control levels477//478// chain-controlled - key is uncontrolled by user479//                    i.e autoincrementing index480//                    can use non-cryptographic hash481// real - key is controlled by user482//        but it is hard to generate enough colliding values, i.e owner of signed txs483//        can use non-cryptographic hash484// controlled - key is completly controlled by users485//              i.e maps with mutable keys486//              should use cryptographic hash487//488// ## User control level downgrade reasons489//490// ?1 - chain-controlled -> controlled491//      collections/tokens can be destroyed, resulting in massive holes492// ?2 - chain-controlled -> controlled493//      same as ?1, but can be only added, resulting in easier exploitation494// ?3 - real -> controlled495//      no confirmation required, so addresses can be easily generated496decl_storage! {497    trait Store for Module<T: Config> as Nft {498499        //#region Private members500        /// Id of next collection501        CreatedCollectionCount: u32;502        /// Used for migrations503        ChainVersion: u64;504        /// Id of last collection token505        /// Collection id (controlled?1)506        ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;507        //#endregion508509        //#region Chain limits struct510        pub ChainLimit get(fn chain_limit) config(): ChainLimits;511        //#endregion512513        //#region Bound counters514        /// Amount of collections destroyed, used for total amount tracking with515        /// CreatedCollectionCount516        DestroyedCollectionCount: u32;517        /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)518        /// Account id (real)519        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;520        //#endregion521522        //#region Basic collections523        /// Collection info524        /// Collection id (controlled?1)525        pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;526        /// List of collection admins527        /// Collection id (controlled?2)528        pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::AccountId>;529        /// Whitelisted collection users530        /// Collection id (controlled?2), user id (controlled?3)531        pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;532        //#endregion533534        /// How many of collection items user have535        /// Collection id (controlled?2), account id (real)536        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;537538        /// Amount of items which spender can transfer out of owners account (via transferFrom)539        /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))540        /// TODO: Off chain worker should remove from this map when token gets removed541        pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;542543        //#region Item collections544        /// Collection id (controlled?2), token id (controlled?1)545        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::AccountId>>;546        /// Collection id (controlled?2), owner (controlled?2)547        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;548        /// Collection id (controlled?2), token id (controlled?1)549        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::AccountId>>;550        //#endregion551552        //#region Index list553        /// Collection id (controlled?2), tokens owner (controlled?2)554        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;555        //#endregion556557        //#region Tokens transfer rate limit baskets558        /// (Collection id (controlled?2), who created (real))559        /// TODO: Off chain worker should remove from this map when collection gets removed560        pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;561        /// Collection id (controlled?2), token id (controlled?2)562        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;563        /// Collection id (controlled?2), owning user (real)564        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;565        /// Collection id (controlled?2), token id (controlled?2)566        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;567        //#endregion568569        /// Variable metadata sponsoring570        /// Collection id (controlled?2), token id (controlled?2)571        pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;572      573        //#region Contract Sponsorship and Ownership574        /// Contract address (real)575        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;576        /// Contract address (real)577        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;578        /// (Contract address(real), caller (real))579        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;580        /// Contract address (real)581        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;582        /// Contract address (real)583        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 584        /// Contract address (real) => Whitelisted user (controlled?3)585        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 586        //#endregion587    }588    add_extra_genesis {589        build(|config: &GenesisConfig<T>| {590            // Modification of storage591            for (_num, _c) in &config.collection_id {592                <Module<T>>::init_collection(_c);593            }594595            for (_num, _c, _i) in &config.nft_item_id {596                <Module<T>>::init_nft_token(*_c, _i);597            }598599            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {600                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);601            }602603            for (_num, _c, _i) in &config.refungible_item_id {604                <Module<T>>::init_refungible_token(*_c, _i);605            }606        })607    }608}609610decl_event!(611    pub enum Event<T>612    where613        AccountId = <T as system::Config>::AccountId,614    {615        /// New collection was created616        /// 617        /// # Arguments618        /// 619        /// * collection_id: Globally unique identifier of newly created collection.620        /// 621        /// * mode: [CollectionMode] converted into u8.622        /// 623        /// * account_id: Collection owner.624        CollectionCreated(CollectionId, u8, AccountId),625626        /// New item was created.627        /// 628        /// # Arguments629        /// 630        /// * collection_id: Id of the collection where item was created.631        /// 632        /// * item_id: Id of an item. Unique within the collection.633        ///634        /// * recipient: Owner of newly created item 635        ItemCreated(CollectionId, TokenId, AccountId),636637        /// Collection item was burned.638        /// 639        /// # Arguments640        /// 641        /// collection_id.642        /// 643        /// item_id: Identifier of burned NFT.644        ItemDestroyed(CollectionId, TokenId),645646        /// Item was transferred647        ///648        /// * collection_id: Id of collection to which item is belong649        ///650        /// * item_id: Id of an item651        ///652        /// * sender: Original owner of item653        ///654        /// * recipient: New owner of item655        ///656        /// * amount: Always 1 for NFT657        Transfer(CollectionId, TokenId, AccountId, AccountId, u128),658659        /// * collection_id660        ///661        /// * item_id662        ///663        /// * sender664        ///665        /// * spender666        ///667        /// * amount668        Approved(CollectionId, TokenId, AccountId, AccountId, u128),669    }670);671672decl_module! {673    pub struct Module<T: Config> for enum Call 674    where 675        origin: T::Origin676    {677        fn deposit_event() = default;678        type Error = Error<T>;679680        fn on_initialize(now: T::BlockNumber) -> Weight {681            0682        }683684        /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.685        /// 686        /// # Permissions687        /// 688        /// * Anyone.689        /// 690        /// # Arguments691        /// 692        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.693        /// 694        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.695        /// 696        /// * token_prefix: UTF-8 string with token prefix.697        /// 698        /// * mode: [CollectionMode] collection type and type dependent data.699        // returns collection ID700        #[weight = <T as Config>::WeightInfo::create_collection()]701        #[transactional]702        pub fn create_collection(origin,703                                 collection_name: Vec<u16>,704                                 collection_description: Vec<u16>,705                                 token_prefix: Vec<u8>,706                                 mode: CollectionMode) -> DispatchResult {707708            // Anyone can create a collection709            let who = ensure_signed(origin)?;710711            // Take a (non-refundable) deposit of collection creation712            let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();713            imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(714                &T::TreasuryAccountId::get(),715                T::CollectionCreationPrice::get(),716            ));717            <T as Config>::Currency::settle(718                &who,719                imbalance,720                WithdrawReasons::TRANSFER,721                ExistenceRequirement::KeepAlive,722            ).map_err(|_| Error::<T>::NoPermission)?;723724            let decimal_points = match mode {725                CollectionMode::Fungible(points) => points,726                _ => 0727            };728729            let chain_limit = ChainLimit::get();730731            let created_count = CreatedCollectionCount::get();732            let destroyed_count = DestroyedCollectionCount::get();733734            // bound Total number of collections735            ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);736737            // check params738            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);739            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);740            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);741            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);742743            // Generate next collection ID744            let next_id = created_count745                .checked_add(1)746                .ok_or(Error::<T>::NumOverflow)?;747748            CreatedCollectionCount::put(next_id);749750            let limits = CollectionLimits {751                sponsored_data_size: chain_limit.custom_data_limit,752                ..Default::default()753            };754755            // Create new collection756            let new_collection = Collection {757                owner: who.clone(),758                name: collection_name,759                mode: mode.clone(),760                mint_mode: false,761                access: AccessMode::Normal,762                description: collection_description,763                decimal_points: decimal_points,764                token_prefix: token_prefix,765                offchain_schema: Vec::new(),766                schema_version: SchemaVersion::ImageURL,767                sponsorship: SponsorshipState::Disabled,768                variable_on_chain_schema: Vec::new(),769                const_on_chain_schema: Vec::new(),770                limits,771            };772773            // Add new collection to map774            <CollectionById<T>>::insert(next_id, new_collection);775776            // call event777            Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who.clone()));778779            Ok(())780        }781782        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.783        /// 784        /// # Permissions785        /// 786        /// * Collection Owner.787        /// 788        /// # Arguments789        /// 790        /// * collection_id: collection to destroy.791        #[weight = <T as Config>::WeightInfo::destroy_collection()]792        #[transactional]793        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {794795            let sender = ensure_signed(origin)?;796            let collection = Self::get_collection(collection_id)?;797            Self::check_owner_permissions(&collection, sender)?;798            if !collection.limits.owner_can_destroy {799                fail!(Error::<T>::NoPermission);800            }801802            <AddressTokens<T>>::remove_prefix(collection_id);803            <Allowances<T>>::remove_prefix(collection_id);804            <Balance<T>>::remove_prefix(collection_id);805            <ItemListIndex>::remove(collection_id);806            <AdminList<T>>::remove(collection_id);807            <CollectionById<T>>::remove(collection_id);808            <WhiteList<T>>::remove_prefix(collection_id);809810            <NftItemList<T>>::remove_prefix(collection_id);811            <FungibleItemList<T>>::remove_prefix(collection_id);812            <ReFungibleItemList<T>>::remove_prefix(collection_id);813814            <NftTransferBasket<T>>::remove_prefix(collection_id);815            <FungibleTransferBasket<T>>::remove_prefix(collection_id);816            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);817818            <VariableMetaDataBasket<T>>::remove_prefix(collection_id);819820            DestroyedCollectionCount::put(DestroyedCollectionCount::get()821                .checked_add(1)822                .ok_or(Error::<T>::NumOverflow)?);823824            Ok(())825        }826827        /// Add an address to white list.828        /// 829        /// # Permissions830        /// 831        /// * Collection Owner832        /// * Collection Admin833        /// 834        /// # Arguments835        /// 836        /// * collection_id.837        /// 838        /// * address.839        #[weight = <T as Config>::WeightInfo::add_to_white_list()]840        #[transactional]841        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{842843            let sender = ensure_signed(origin)?;844            let collection = Self::get_collection(collection_id)?;845            Self::check_owner_or_admin_permissions(&collection, sender)?;846847            <WhiteList<T>>::insert(collection_id, address, true);848            849            Ok(())850        }851852        /// Remove an address from white list.853        /// 854        /// # Permissions855        /// 856        /// * Collection Owner857        /// * Collection Admin858        /// 859        /// # Arguments860        /// 861        /// * collection_id.862        /// 863        /// * address.864        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]865        #[transactional]866        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{867868            let sender = ensure_signed(origin)?;869            let collection = Self::get_collection(collection_id)?;870            Self::check_owner_or_admin_permissions(&collection, sender)?;871872            <WhiteList<T>>::remove(collection_id, address);873874            Ok(())875        }876877        /// Toggle between normal and white list access for the methods with access for `Anyone`.878        /// 879        /// # Permissions880        /// 881        /// * Collection Owner.882        /// 883        /// # Arguments884        /// 885        /// * collection_id.886        /// 887        /// * mode: [AccessMode]888        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]889        #[transactional]890        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult891        {892            let sender = ensure_signed(origin)?;893894            let mut target_collection = Self::get_collection(collection_id)?;895            Self::check_owner_permissions(&target_collection, sender)?;896            target_collection.access = mode;897            Self::save_collection(target_collection);898899            Ok(())900        }901902        /// Allows Anyone to create tokens if:903        /// * White List is enabled, and904        /// * Address is added to white list, and905        /// * This method was called with True parameter906        /// 907        /// # Permissions908        /// * Collection Owner909        ///910        /// # Arguments911        /// 912        /// * collection_id.913        /// 914        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.915        #[weight = <T as Config>::WeightInfo::set_mint_permission()]916        #[transactional]917        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult918        {919            let sender = ensure_signed(origin)?;920921            let mut target_collection = Self::get_collection(collection_id)?;922            Self::check_owner_permissions(&target_collection, sender)?;923            target_collection.mint_mode = mint_permission;924            Self::save_collection(target_collection);925926            Ok(())927        }928929        /// Change the owner of the collection.930        /// 931        /// # Permissions932        /// 933        /// * Collection Owner.934        /// 935        /// # Arguments936        /// 937        /// * collection_id.938        /// 939        /// * new_owner.940        #[weight = <T as Config>::WeightInfo::change_collection_owner()]941        #[transactional]942        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {943944            let sender = ensure_signed(origin)?;945            let mut target_collection = Self::get_collection(collection_id)?;946            Self::check_owner_permissions(&target_collection, sender)?;947            target_collection.owner = new_owner;948            Self::save_collection(target_collection);949950            Ok(())951        }952953        /// Adds an admin of the Collection.954        /// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership. 955        /// 956        /// # Permissions957        /// 958        /// * Collection Owner.959        /// * Collection Admin.960        /// 961        /// # Arguments962        /// 963        /// * collection_id: ID of the Collection to add admin for.964        /// 965        /// * new_admin_id: Address of new admin to add.966        #[weight = <T as Config>::WeightInfo::add_collection_admin()]967        #[transactional]968        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {969970            let sender = ensure_signed(origin)?;971            let collection = Self::get_collection(collection_id)?;972            Self::check_owner_or_admin_permissions(&collection, sender)?;973            let mut admin_arr = <AdminList<T>>::get(collection_id);974975            match admin_arr.binary_search(&new_admin_id) {976                Ok(_) => {},977                Err(idx) => {978                    let limits = ChainLimit::get();979                    ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);980                    admin_arr.insert(idx, new_admin_id);981                    <AdminList<T>>::insert(collection_id, admin_arr);982                }983            }984            Ok(())985        }986987        /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.988        ///989        /// # Permissions990        /// 991        /// * Collection Owner.992        /// * Collection Admin.993        /// 994        /// # Arguments995        /// 996        /// * collection_id: ID of the Collection to remove admin for.997        /// 998        /// * account_id: Address of admin to remove.999        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]1000        #[transactional]1001        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {10021003            let sender = ensure_signed(origin)?;1004            let collection = Self::get_collection(collection_id)?;1005            Self::check_owner_or_admin_permissions(&collection, sender)?;1006            let mut admin_arr = <AdminList<T>>::get(collection_id);10071008            match admin_arr.binary_search(&account_id) {1009                Ok(idx) => {1010                    admin_arr.remove(idx);1011                    <AdminList<T>>::insert(collection_id, admin_arr);1012                },1013                Err(_) => {}1014            }1015            Ok(())1016        }10171018        /// # Permissions1019        /// 1020        /// * Collection Owner1021        /// 1022        /// # Arguments1023        /// 1024        /// * collection_id.1025        /// 1026        /// * new_sponsor.1027        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]1028        #[transactional]1029        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {10301031            let sender = ensure_signed(origin)?;1032            let mut target_collection = Self::get_collection(collection_id)?;1033            Self::check_owner_permissions(&target_collection, sender)?;10341035            target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);1036            Self::save_collection(target_collection);10371038            Ok(())1039        }10401041        /// # Permissions1042        /// 1043        /// * Sponsor.1044        /// 1045        /// # Arguments1046        /// 1047        /// * collection_id.1048        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]1049        #[transactional]1050        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {10511052            let sender = ensure_signed(origin)?;10531054            let mut target_collection = Self::get_collection(collection_id)?;1055            ensure!(1056                target_collection.sponsorship.pending_sponsor() == Some(&sender),1057                Error::<T>::ConfirmUnsetSponsorFail1058            );10591060            target_collection.sponsorship = SponsorshipState::Confirmed(sender);1061            Self::save_collection(target_collection);10621063            Ok(())1064        }10651066        /// Switch back to pay-per-own-transaction model.1067        ///1068        /// # Permissions1069        ///1070        /// * Collection owner.1071        /// 1072        /// # Arguments1073        /// 1074        /// * collection_id.1075        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]1076        #[transactional]1077        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {10781079            let sender = ensure_signed(origin)?;10801081            let mut target_collection = Self::get_collection(collection_id)?;1082            Self::check_owner_permissions(&target_collection, sender)?;10831084            target_collection.sponsorship = SponsorshipState::Disabled;1085            Self::save_collection(target_collection);10861087            Ok(())1088        }10891090        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.1091        /// 1092        /// # Permissions1093        /// 1094        /// * Collection Owner.1095        /// * Collection Admin.1096        /// * Anyone if1097        ///     * White List is enabled, and1098        ///     * Address is added to white list, and1099        ///     * MintPermission is enabled (see SetMintPermission method)1100        /// 1101        /// # Arguments1102        /// 1103        /// * collection_id: ID of the collection.1104        /// 1105        /// * owner: Address, initial owner of the NFT.1106        ///1107        /// * data: Token data to store on chain.1108        // #[weight =1109        // (130_000_000 as Weight)1110        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))1111        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))1112        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]11131114        #[weight = <T as Config>::WeightInfo::create_item(data.len())]1115        #[transactional]1116        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1117            let sender = ensure_signed(origin)?;1118            Self::create_item_internal(sender, collection_id, owner, data)1119        }11201121        /// This method creates multiple items in a collection created with CreateCollection method.1122        /// 1123        /// # Permissions1124        /// 1125        /// * Collection Owner.1126        /// * Collection Admin.1127        /// * Anyone if1128        ///     * White List is enabled, and1129        ///     * Address is added to white list, and1130        ///     * MintPermission is enabled (see SetMintPermission method)1131        /// 1132        /// # Arguments1133        /// 1134        /// * collection_id: ID of the collection.1135        /// 1136        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].1137        /// 1138        /// * owner: Address, initial owner of the NFT.1139        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1140                               .map(|data| { data.len() })1141                               .sum())]1142        #[transactional]1143        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {11441145            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1146            let sender = ensure_signed(origin)?;11471148            let target_collection = Self::get_collection(collection_id)?;11491150            Self::can_create_items_in_collection(&target_collection, &sender, &owner, items_data.len() as u32)?;11511152            for data in &items_data {1153                Self::validate_create_item_args(&target_collection, data)?;1154            }1155            for data in &items_data {1156                Self::create_item_no_validation(&target_collection, owner.clone(), data.clone())?;1157            }11581159            Ok(())1160        }11611162        /// Destroys a concrete instance of NFT.1163        /// 1164        /// # Permissions1165        /// 1166        /// * Collection Owner.1167        /// * Collection Admin.1168        /// * Current NFT Owner.1169        /// 1170        /// # Arguments1171        /// 1172        /// * collection_id: ID of the collection.1173        /// 1174        /// * item_id: ID of NFT to burn.1175        #[weight = <T as Config>::WeightInfo::burn_item()]1176        #[transactional]1177        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {11781179            let sender = ensure_signed(origin)?;11801181            // Transfer permissions check1182            let target_collection = Self::get_collection(collection_id)?;1183            ensure!(1184                Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1185                (1186                    target_collection.limits.owner_can_transfer &&1187                    Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1188                ),1189                Error::<T>::NoPermission1190            );11911192            if target_collection.access == AccessMode::WhiteList {1193                Self::check_white_list(&target_collection, &sender)?;1194            }11951196            match target_collection.mode1197            {1198                CollectionMode::NFT => Self::burn_nft_item(&target_collection, item_id)?,1199                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, &target_collection, value)?,1200                CollectionMode::ReFungible  => Self::burn_refungible_item(&target_collection, item_id, &sender)?,1201                _ => ()1202            };12031204            // call event1205            Self::deposit_event(RawEvent::ItemDestroyed(target_collection.id, item_id));12061207            Ok(())1208        }12091210        /// Change ownership of the token.1211        /// 1212        /// # Permissions1213        /// 1214        /// * Collection Owner1215        /// * Collection Admin1216        /// * Current NFT owner1217        ///1218        /// # Arguments1219        /// 1220        /// * recipient: Address of token recipient.1221        /// 1222        /// * collection_id.1223        /// 1224        /// * item_id: ID of the item1225        ///     * Non-Fungible Mode: Required.1226        ///     * Fungible Mode: Ignored.1227        ///     * Re-Fungible Mode: Required.1228        /// 1229        /// * value: Amount to transfer.1230        ///     * Non-Fungible Mode: Ignored1231        ///     * Fungible Mode: Must specify transferred amount1232        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1233        #[weight = <T as Config>::WeightInfo::transfer()]1234        #[transactional]1235        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1236            let sender = ensure_signed(origin)?;1237            let collection = Self::get_collection(collection_id)?;12381239            Self::transfer_internal(sender, recipient, &collection, item_id, value)1240        }12411242        /// Set, change, or remove approved address to transfer the ownership of the NFT.1243        /// 1244        /// # Permissions1245        /// 1246        /// * Collection Owner1247        /// * Collection Admin1248        /// * Current NFT owner1249        /// 1250        /// # Arguments1251        /// 1252        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1253        /// 1254        /// * collection_id.1255        /// 1256        /// * item_id: ID of the item.1257        #[weight = <T as Config>::WeightInfo::approve()]1258        #[transactional]1259        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {12601261            let sender = ensure_signed(origin)?;1262            let target_collection = Self::get_collection(collection_id)?;12631264            Self::token_exists(&target_collection, item_id)?;12651266            // Transfer permissions check1267            let bypasses_limits = target_collection.limits.owner_can_transfer &&1268                Self::is_owner_or_admin_permissions(1269                    &target_collection,1270                    sender.clone(),1271                );12721273            let allowance_limit = if bypasses_limits {1274                None1275            } else if let Some(amount) = Self::owned_amount(1276                sender.clone(),1277                &target_collection,1278                item_id,1279            ) {1280                Some(amount)1281            } else {1282                fail!(Error::<T>::NoPermission);1283            };12841285            if target_collection.access == AccessMode::WhiteList {1286                Self::check_white_list(&target_collection, &sender)?;1287                Self::check_white_list(&target_collection, &spender)?;1288            }12891290            let allowance: u128 = amount1291                .checked_add(<Allowances<T>>::get(collection_id, (item_id, &sender, &spender)))1292                .ok_or(Error::<T>::NumOverflow)?;1293            if let Some(limit) = allowance_limit {1294                ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1295            }1296            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);12971298            Self::deposit_event(RawEvent::Approved(target_collection.id, item_id, sender, spender, allowance));1299            Ok(())1300        }1301        1302        /// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.1303        /// 1304        /// # Permissions1305        /// * Collection Owner1306        /// * Collection Admin1307        /// * Current NFT owner1308        /// * Address approved by current NFT owner1309        /// 1310        /// # Arguments1311        /// 1312        /// * from: Address that owns token.1313        /// 1314        /// * recipient: Address of token recipient.1315        /// 1316        /// * collection_id.1317        /// 1318        /// * item_id: ID of the item.1319        /// 1320        /// * value: Amount to transfer.1321        #[weight = <T as Config>::WeightInfo::transfer_from()]1322        #[transactional]1323        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {13241325            let sender = ensure_signed(origin)?;1326            let target_collection = Self::get_collection(collection_id)?;13271328            // Check approval1329            let approval: u128 = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));13301331            // Limits check1332            Self::is_correct_transfer(&target_collection, &recipient)?;13331334            // Transfer permissions check         1335            ensure!(1336                approval >= value || 1337                (1338                    target_collection.limits.owner_can_transfer &&1339                    Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1340                ),1341                Error::<T>::NoPermission1342            );13431344            if target_collection.access == AccessMode::WhiteList {1345                Self::check_white_list(&target_collection, &sender)?;1346                Self::check_white_list(&target_collection, &recipient)?;1347            }13481349            // Reduce approval by transferred amount or remove if remaining approval drops to 01350            if approval.saturating_sub(value) > 0 {1351                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1352            }1353            else {1354                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1355            }13561357            match target_collection.mode1358            {1359                CollectionMode::NFT => Self::transfer_nft(&target_collection, item_id, from.clone(), recipient.clone())?,1360                CollectionMode::Fungible(_)  => Self::transfer_fungible(&target_collection, value, &from, &recipient)?,1361                CollectionMode::ReFungible  => Self::transfer_refungible(&target_collection, item_id, value, from.clone(), recipient.clone())?,1362                _ => ()1363            };13641365            Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, from, recipient, value));1366            Ok(())1367        }13681369        // #[weight = 0]1370        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {13711372        //     // let no_perm_mes = "You do not have permissions to modify this collection";1373        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1374        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1375        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);13761377        //     // // on_nft_received  call13781379        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;13801381        //     Ok(())1382        // }13831384        /// Set off-chain data schema.1385        /// 1386        /// # Permissions1387        /// 1388        /// * Collection Owner1389        /// * Collection Admin1390        /// 1391        /// # Arguments1392        /// 1393        /// * collection_id.1394        /// 1395        /// * schema: String representing the offchain data schema.1396        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1397        #[transactional]1398        pub fn set_variable_meta_data (1399            origin,1400            collection_id: CollectionId,1401            item_id: TokenId,1402            data: Vec<u8>1403        ) -> DispatchResult {1404            let sender = ensure_signed(origin)?;1405            1406            let target_collection = Self::get_collection(collection_id)?;1407            Self::token_exists(&target_collection, item_id)?;14081409            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);14101411            // Modify permissions check1412            ensure!(Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1413                Self::is_owner_or_admin_permissions(&target_collection, sender.clone()),1414                Error::<T>::NoPermission);14151416            match target_collection.mode1417            {1418                CollectionMode::NFT => Self::set_nft_variable_data(&target_collection, item_id, data)?,1419                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(&target_collection, item_id, data)?,1420                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1421                _ => fail!(Error::<T>::UnexpectedCollectionType)1422            };14231424            Ok(())1425        }1426 1427        /// Set schema standard1428        /// ImageURL1429        /// Unique1430        /// 1431        /// # Permissions1432        /// 1433        /// * Collection Owner1434        /// * Collection Admin1435        /// 1436        /// # Arguments1437        /// 1438        /// * collection_id.1439        /// 1440        /// * schema: SchemaVersion: enum1441        #[weight = <T as Config>::WeightInfo::set_schema_version()]1442        #[transactional]1443        pub fn set_schema_version(1444            origin,1445            collection_id: CollectionId,1446            version: SchemaVersion1447        ) -> DispatchResult {1448            let sender = ensure_signed(origin)?;1449            let mut target_collection = Self::get_collection(collection_id)?;1450            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;1451            target_collection.schema_version = version;1452            Self::save_collection(target_collection);14531454            Ok(())1455        }14561457        /// Set off-chain data schema.1458        /// 1459        /// # Permissions1460        /// 1461        /// * Collection Owner1462        /// * Collection Admin1463        /// 1464        /// # Arguments1465        /// 1466        /// * collection_id.1467        /// 1468        /// * schema: String representing the offchain data schema.1469        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1470        #[transactional]1471        pub fn set_offchain_schema(1472            origin,1473            collection_id: CollectionId,1474            schema: Vec<u8>1475        ) -> DispatchResult {1476            let sender = ensure_signed(origin)?;1477            let mut target_collection = Self::get_collection(collection_id)?;1478            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;14791480            // check schema limit1481            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");14821483            target_collection.offchain_schema = schema;1484            Self::save_collection(target_collection);14851486            Ok(())1487        }14881489        /// Set const on-chain data schema.1490        /// 1491        /// # Permissions1492        /// 1493        /// * Collection Owner1494        /// * Collection Admin1495        /// 1496        /// # Arguments1497        /// 1498        /// * collection_id.1499        /// 1500        /// * schema: String representing the const on-chain data schema.1501        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1502        #[transactional]1503        pub fn set_const_on_chain_schema (1504            origin,1505            collection_id: CollectionId,1506            schema: Vec<u8>1507        ) -> DispatchResult {1508            let sender = ensure_signed(origin)?;1509            let mut target_collection = Self::get_collection(collection_id)?;1510            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;15111512            // check schema limit1513            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");15141515            target_collection.const_on_chain_schema = schema;1516            Self::save_collection(target_collection);15171518            Ok(())1519        }15201521        /// Set variable on-chain data schema.1522        /// 1523        /// # Permissions1524        /// 1525        /// * Collection Owner1526        /// * Collection Admin1527        /// 1528        /// # Arguments1529        /// 1530        /// * collection_id.1531        /// 1532        /// * schema: String representing the variable on-chain data schema.1533        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1534        #[transactional]1535        pub fn set_variable_on_chain_schema (1536            origin,1537            collection_id: CollectionId,1538            schema: Vec<u8>1539        ) -> DispatchResult {1540            let sender = ensure_signed(origin)?;1541            let mut target_collection = Self::get_collection(collection_id)?;1542            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;15431544            // check schema limit1545            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");15461547            target_collection.variable_on_chain_schema = schema;1548            Self::save_collection(target_collection);15491550            Ok(())1551        }15521553        // Sudo permissions function1554        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1555        #[transactional]1556        pub fn set_chain_limits(1557            origin,1558            limits: ChainLimits1559        ) -> DispatchResult {15601561            #[cfg(not(feature = "runtime-benchmarks"))]1562            ensure_root(origin)?;15631564            <ChainLimit>::put(limits);1565            Ok(())1566        }15671568        /// Enable smart contract self-sponsoring.1569        /// 1570        /// # Permissions1571        /// 1572        /// * Contract Owner1573        /// 1574        /// # Arguments1575        /// 1576        /// * contract address1577        /// * enable flag1578        /// 1579        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1580        #[transactional]1581        pub fn enable_contract_sponsoring(1582            origin,1583            contract_address: T::AccountId,1584            enable: bool1585        ) -> DispatchResult {15861587            let sender = ensure_signed(origin)?;15881589            #[cfg(feature = "runtime-benchmarks")]1590            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15911592            Self::ensure_contract_owned(sender, &contract_address)?;15931594            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1595            Ok(())1596        }15971598        /// Set the rate limit for contract sponsoring to specified number of blocks.1599        /// 1600        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1601        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1602        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1603        /// from contract endowment if there are at least B blocks between such transactions. 1604        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1605        /// 1606        /// # Permissions1607        /// 1608        /// * Contract Owner1609        /// 1610        /// # Arguments1611        /// 1612        /// -`contract_address`: Address of the contract to sponsor1613        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1614        /// 1615        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1616        #[transactional]1617        pub fn set_contract_sponsoring_rate_limit(1618            origin,1619            contract_address: T::AccountId,1620            rate_limit: T::BlockNumber1621        ) -> DispatchResult {1622            let sender = ensure_signed(origin)?;16231624            #[cfg(feature = "runtime-benchmarks")]1625            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16261627            Self::ensure_contract_owned(sender, &contract_address)?;1628            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1629            Ok(())1630        }16311632        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1633        /// 1634        /// # Permissions1635        /// 1636        /// * Address that deployed smart contract.1637        /// 1638        /// # Arguments1639        /// 1640        /// -`contract_address`: Address of the contract.1641        /// 1642        /// - `enable`: .  1643        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1644        #[transactional]1645        pub fn toggle_contract_white_list(1646            origin,1647            contract_address: T::AccountId,1648            enable: bool1649        ) -> DispatchResult {1650            let sender = ensure_signed(origin)?;16511652            #[cfg(feature = "runtime-benchmarks")]1653            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16541655            Self::ensure_contract_owned(sender, &contract_address)?;1656            if enable {1657                <ContractWhiteListEnabled<T>>::insert(contract_address, true);1658            } else {1659                <ContractWhiteListEnabled<T>>::remove(contract_address);1660            }1661            Ok(())1662        }1663        1664        /// Add an address to smart contract white list.1665        /// 1666        /// # Permissions1667        /// 1668        /// * Address that deployed smart contract.1669        /// 1670        /// # Arguments1671        /// 1672        /// -`contract_address`: Address of the contract.1673        ///1674        /// -`account_address`: Address to add.1675        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1676        #[transactional]1677        pub fn add_to_contract_white_list(1678            origin,1679            contract_address: T::AccountId,1680            account_address: T::AccountId1681        ) -> DispatchResult {1682            let sender = ensure_signed(origin)?;16831684            #[cfg(feature = "runtime-benchmarks")]1685            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1686            1687            Self::ensure_contract_owned(sender, &contract_address)?;      1688            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1689            Ok(())1690        }16911692        /// Remove an address from smart contract white list.1693        /// 1694        /// # Permissions1695        /// 1696        /// * Address that deployed smart contract.1697        /// 1698        /// # Arguments1699        /// 1700        /// -`contract_address`: Address of the contract.1701        ///1702        /// -`account_address`: Address to remove.1703        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1704        #[transactional]1705        pub fn remove_from_contract_white_list(1706            origin,1707            contract_address: T::AccountId,1708            account_address: T::AccountId1709        ) -> DispatchResult {1710            let sender = ensure_signed(origin)?;17111712            #[cfg(feature = "runtime-benchmarks")]1713            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());17141715            Self::ensure_contract_owned(sender, &contract_address)?;1716            <ContractWhiteList<T>>::remove(contract_address, account_address);1717            Ok(())1718        }17191720        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1721        #[transactional]1722        pub fn set_collection_limits(1723            origin,1724            collection_id: u32,1725            new_limits: CollectionLimits<T::BlockNumber>,1726        ) -> DispatchResult {1727            let sender = ensure_signed(origin)?;1728            let mut target_collection = Self::get_collection(collection_id)?;1729            Self::check_owner_permissions(&target_collection, sender.clone())?;1730            let old_limits = &target_collection.limits;1731            let chain_limits = ChainLimit::get();17321733            // collection bounds1734            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1735                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1736                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1737                Error::<T>::CollectionLimitBoundsExceeded);17381739            // token_limit   check  prev1740            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1741            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);17421743            ensure!(1744                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1745                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1746                Error::<T>::OwnerPermissionsCantBeReverted,1747            );17481749            target_collection.limits = new_limits;1750            Self::save_collection(target_collection);17511752            Ok(())1753        } 1754    }1755}17561757impl<T: Config> Module<T> {1758    pub fn create_item_internal(sender: T::AccountId, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1759        let target_collection = Self::get_collection(collection_id)?;17601761        Self::can_create_items_in_collection(&target_collection, &sender, &owner, 1)?;1762        Self::validate_create_item_args(&target_collection, &data)?;1763        Self::create_item_no_validation(&target_collection, owner, data)?;17641765        Ok(())1766    }17671768    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1769        // Limits check1770        Self::is_correct_transfer(target_collection, &recipient)?;17711772        // Transfer permissions check1773        ensure!(Self::is_item_owner(sender.clone(), target_collection, item_id) ||1774            Self::is_owner_or_admin_permissions(target_collection, sender.clone()),1775            Error::<T>::NoPermission);17761777        if target_collection.access == AccessMode::WhiteList {1778            Self::check_white_list(target_collection, &sender)?;1779            Self::check_white_list(target_collection, &recipient)?;1780        }17811782        match target_collection.mode1783        {1784            CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1785            CollectionMode::Fungible(_)  => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1786            CollectionMode::ReFungible  => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1787            _ => ()1788        };17891790        Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender, recipient, value));17911792        Ok(())1793    }179417951796    fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::AccountId) -> DispatchResult {1797        let collection_id = collection.id;17981799        // check token limit and account token limit1800        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1801        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1802        1803        Ok(())1804    }18051806    fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::AccountId, owner: &T::AccountId, amount: u32) -> DispatchResult {1807        let collection_id = collection.id;18081809        // check token limit and account token limit1810        let total_items: u32 = ItemListIndex::get(collection_id)1811            .checked_add(amount)1812            .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1813        let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner).len() as u32)1814            .checked_add(amount)1815            .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1816        ensure!(collection.limits.token_limit >= total_items,  Error::<T>::CollectionTokenLimitExceeded);1817        ensure!(collection.limits.account_token_ownership_limit >= account_items,  Error::<T>::AccountTokenLimitExceeded);18181819        if !Self::is_owner_or_admin_permissions(collection, sender.clone()) {1820            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1821            Self::check_white_list(collection, owner)?;1822            Self::check_white_list(collection, sender)?;1823        }18241825        Ok(())1826    }18271828    fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1829        match target_collection.mode1830        {1831            CollectionMode::NFT => {1832                if let CreateItemData::NFT(data) = data {1833                    // check sizes1834                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1835                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1836                } else {1837                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1838                }1839            },1840            CollectionMode::Fungible(_) => {1841                if let CreateItemData::Fungible(_) = data {1842                } else {1843                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1844                }1845            },1846            CollectionMode::ReFungible => {1847                if let CreateItemData::ReFungible(data) = data {18481849                    // check sizes1850                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1851                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);18521853                    // Check refungibility limits1854                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1855                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1856                } else {1857                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1858                }1859            },1860            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1861        };18621863        Ok(())1864    }18651866    fn create_item_no_validation(collection: &CollectionHandle<T>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1867        match data1868        {1869            CreateItemData::NFT(data) => {1870                let item = NftItemType {1871                    owner: owner.clone(),1872                    const_data: data.const_data,1873                    variable_data: data.variable_data1874                };18751876                Self::add_nft_item(collection, item)?;1877            },1878            CreateItemData::Fungible(data) => {1879                Self::add_fungible_item(collection, &owner, data.value)?;1880            },1881            CreateItemData::ReFungible(data) => {1882                let mut owner_list = Vec::new();1883                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});18841885                let item = ReFungibleItemType {1886                    owner: owner_list,1887                    const_data: data.const_data,1888                    variable_data: data.variable_data1889                };18901891                Self::add_refungible_item(collection, item)?;1892            }1893        };18941895        Ok(())1896    }18971898    fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::AccountId, value: u128) -> DispatchResult {1899        let collection_id = collection.id;19001901        // Does new owner already have an account?1902        let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner).value;19031904        // Mint 1905        let item = FungibleItemType {1906            value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1907        };1908        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);19091910        // Update balance1911        let new_balance = <Balance<T>>::get(collection_id, owner)1912            .checked_add(value)1913            .ok_or(Error::<T>::NumOverflow)?;1914        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);19151916        Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1917        Ok(())1918    }19191920    fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1921        let collection_id = collection.id;19221923        let current_index = <ItemListIndex>::get(collection_id)1924            .checked_add(1)1925            .ok_or(Error::<T>::NumOverflow)?;1926        let itemcopy = item.clone();19271928        ensure!(1929            item.owner.len() == 1,1930            Error::<T>::BadCreateRefungibleCall,1931        );1932        let item_owner = item.owner.first().expect("only one owner is defined");19331934        let value = item_owner.fraction;1935        let owner = item_owner.owner.clone();19361937        Self::add_token_index(collection_id, current_index, &owner)?;19381939        <ItemListIndex>::insert(collection_id, current_index);1940        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);19411942        // Update balance1943        let new_balance = <Balance<T>>::get(collection_id, &owner)1944            .checked_add(value)1945            .ok_or(Error::<T>::NumOverflow)?;1946        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);19471948        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1949        Ok(())1950    }19511952    fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::AccountId>) -> DispatchResult {1953        let collection_id = collection.id;19541955        let current_index = <ItemListIndex>::get(collection_id)1956            .checked_add(1)1957            .ok_or(Error::<T>::NumOverflow)?;19581959        let item_owner = item.owner.clone();1960        Self::add_token_index(collection_id, current_index, &item.owner)?;19611962        <ItemListIndex>::insert(collection_id, current_index);1963        <NftItemList<T>>::insert(collection_id, current_index, item);19641965        // Update balance1966        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1967            .checked_add(1)1968            .ok_or(Error::<T>::NumOverflow)?;1969        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);19701971        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));1972        Ok(())1973    }19741975    fn burn_refungible_item(1976        collection: &CollectionHandle<T>,1977        item_id: TokenId,1978        owner: &T::AccountId,1979    ) -> DispatchResult {1980        let collection_id = collection.id;19811982        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1983            .ok_or(Error::<T>::TokenNotFound)?;1984        let rft_balance = token1985            .owner1986            .iter()1987            .find(|&i| i.owner == *owner)1988            .ok_or(Error::<T>::TokenNotFound)?;1989        Self::remove_token_index(collection_id, item_id, owner)?;19901991        // update balance1992        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1993            .checked_sub(rft_balance.fraction)1994            .ok_or(Error::<T>::NumOverflow)?;1995        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);19961997        // Re-create owners list with sender removed1998        let index = token1999            .owner2000            .iter()2001            .position(|i| i.owner == *owner)2002            .expect("owned item is exists");2003        token.owner.remove(index);2004        let owner_count = token.owner.len();20052006        // Burn the token completely if this was the last (only) owner2007        if owner_count == 0 {2008            <ReFungibleItemList<T>>::remove(collection_id, item_id);2009            <VariableMetaDataBasket<T>>::remove(collection_id, item_id);2010        }2011        else {2012            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);2013        }20142015        Ok(())2016    }20172018    fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {2019        let collection_id = collection.id;20202021        let item = <NftItemList<T>>::get(collection_id, item_id)2022            .ok_or(Error::<T>::TokenNotFound)?;2023        Self::remove_token_index(collection_id, item_id, &item.owner)?;20242025        // update balance2026        let new_balance = <Balance<T>>::get(collection_id, &item.owner)2027            .checked_sub(1)2028            .ok_or(Error::<T>::NumOverflow)?;2029        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);2030        <NftItemList<T>>::remove(collection_id, item_id);2031        <VariableMetaDataBasket<T>>::remove(collection_id, item_id);20322033        Ok(())2034    }20352036    fn burn_fungible_item(owner: &T::AccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {2037        let collection_id = collection.id;20382039        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);2040        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);20412042        // update balance2043        let new_balance = <Balance<T>>::get(collection_id, owner)2044            .checked_sub(value)2045            .ok_or(Error::<T>::NumOverflow)?;2046        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);20472048        if balance.value - value > 0 {2049            balance.value -= value;2050            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);2051        }2052        else {2053            <FungibleItemList<T>>::remove(collection_id, owner);2054        }20552056        Ok(())2057    }20582059    pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {2060        Ok(<CollectionById<T>>::get(collection_id)2061            .map(|collection| CollectionHandle {2062                id: collection_id,2063                collection2064            })2065            .ok_or(Error::<T>::CollectionNotFound)?)2066    }20672068    fn save_collection(collection: CollectionHandle<T>) {2069        <CollectionById<T>>::insert(collection.id, collection.collection);2070    }20712072    fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: T::AccountId) -> DispatchResult {2073        ensure!(2074            subject == target_collection.owner,2075            Error::<T>::NoPermission2076        );20772078        Ok(())2079    }20802081    fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: T::AccountId) -> bool {2082        subject == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)2083    }20842085    fn check_owner_or_admin_permissions(2086        collection: &CollectionHandle<T>,2087        subject: T::AccountId,2088    ) -> DispatchResult {2089        ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);20902091        Ok(())2092    }20932094    fn owned_amount(2095        subject: T::AccountId,2096        target_collection: &CollectionHandle<T>,2097        item_id: TokenId,2098    ) -> Option<u128> {2099        let collection_id = target_collection.id;21002101        match target_collection.mode {2102            CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == subject)2103                .then(|| 1),2104            CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject)2105                .value),2106            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?2107                .owner2108                .iter()2109                .find(|i| i.owner == subject)2110                .map(|i| i.fraction),2111            CollectionMode::Invalid => None,2112        }2113    }21142115    fn is_item_owner(subject: T::AccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {2116        match target_collection.mode {2117            CollectionMode::Fungible(_) => true,2118            _ => Self::owned_amount(subject, target_collection, item_id).is_some(),2119        }2120    }21212122    fn check_white_list(collection: &CollectionHandle<T>, address: &T::AccountId) -> DispatchResult {2123        let collection_id = collection.id;21242125        let mes = Error::<T>::AddresNotInWhiteList;2126        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);21272128        Ok(())2129    }21302131    /// Check if token exists. In case of Fungible, check if there is an entry for 2132    /// the owner in fungible balances double map2133    fn token_exists(2134        target_collection: &CollectionHandle<T>,2135        item_id: TokenId,2136    ) -> DispatchResult {2137        let collection_id = target_collection.id;2138        let exists = match target_collection.mode2139        {2140            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2141            CollectionMode::Fungible(_)  => true,2142            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2143            _ => false2144        };21452146        ensure!(exists == true, Error::<T>::TokenNotFound);2147        Ok(())2148    }21492150    fn transfer_fungible(2151        collection: &CollectionHandle<T>,2152        value: u128,2153        owner: &T::AccountId,2154        recipient: &T::AccountId,2155    ) -> DispatchResult {2156        let collection_id = collection.id;21572158        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);2159        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);21602161        // Send balance to recipient (updates balanceOf of recipient)2162        Self::add_fungible_item(collection, recipient, value)?;21632164        // update balanceOf of sender2165        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);21662167        // Reduce or remove sender2168        if balance.value == value {2169            <FungibleItemList<T>>::remove(collection_id, owner);2170        }2171        else {2172            balance.value -= value;2173            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);2174        }21752176        Ok(())2177    }21782179    fn transfer_refungible(2180        collection: &CollectionHandle<T>,2181        item_id: TokenId,2182        value: u128,2183        owner: T::AccountId,2184        new_owner: T::AccountId,2185    ) -> DispatchResult {2186        let collection_id = collection.id;2187        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2188            .ok_or(Error::<T>::TokenNotFound)?;21892190        let item = full_item2191            .owner2192            .iter()2193            .filter(|i| i.owner == owner)2194            .next()2195            .ok_or(Error::<T>::TokenNotFound)?;2196        let amount = item.fraction;21972198        ensure!(amount >= value, Error::<T>::TokenValueTooLow);21992200        // update balance2201        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2202            .checked_sub(value)2203            .ok_or(Error::<T>::NumOverflow)?;2204        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);22052206        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())2207            .checked_add(value)2208            .ok_or(Error::<T>::NumOverflow)?;2209        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);22102211        let old_owner = item.owner.clone();2212        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);22132214        // transfer2215        if amount == value && !new_owner_has_account {2216            // change owner2217            // new owner do not have account2218            let mut new_full_item = full_item.clone();2219            new_full_item2220                .owner2221                .iter_mut()2222                .find(|i| i.owner == owner)2223                .expect("old owner does present in refungible")2224                .owner = new_owner.clone();2225            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);22262227            // update index collection2228            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2229        } else {2230            let mut new_full_item = full_item.clone();2231            new_full_item2232                .owner2233                .iter_mut()2234                .find(|i| i.owner == owner)2235                .expect("old owner does present in refungible")2236                .fraction -= value;22372238            // separate amount2239            if new_owner_has_account {2240                // new owner has account2241                new_full_item2242                    .owner2243                    .iter_mut()2244                    .find(|i| i.owner == new_owner)2245                    .expect("new owner has account")2246                    .fraction += value;2247            } else {2248                // new owner do not have account2249                new_full_item.owner.push(Ownership {2250                    owner: new_owner.clone(),2251                    fraction: value,2252                });2253                Self::add_token_index(collection_id, item_id, &new_owner)?;2254            }22552256            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2257        }22582259        Ok(())2260    }22612262    fn transfer_nft(2263        collection: &CollectionHandle<T>,2264        item_id: TokenId,2265        sender: T::AccountId,2266        new_owner: T::AccountId,2267    ) -> DispatchResult {2268        let collection_id = collection.id;2269        let mut item = <NftItemList<T>>::get(collection_id, item_id)2270            .ok_or(Error::<T>::TokenNotFound)?;22712272        ensure!(2273            sender == item.owner,2274            Error::<T>::MustBeTokenOwner2275        );22762277        // update balance2278        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2279            .checked_sub(1)2280            .ok_or(Error::<T>::NumOverflow)?;2281        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);22822283        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2284            .checked_add(1)2285            .ok_or(Error::<T>::NumOverflow)?;2286        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);22872288        // change owner2289        let old_owner = item.owner.clone();2290        item.owner = new_owner.clone();2291        <NftItemList<T>>::insert(collection_id, item_id, item);22922293        // update index collection2294        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;22952296        Ok(())2297    }2298    2299    fn set_re_fungible_variable_data(2300        collection: &CollectionHandle<T>,2301        item_id: TokenId,2302        data: Vec<u8>2303    ) -> DispatchResult {2304        let collection_id = collection.id;2305        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2306            .ok_or(Error::<T>::TokenNotFound)?;23072308        item.variable_data = data;23092310        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);23112312        Ok(())2313    }23142315    fn set_nft_variable_data(2316        collection: &CollectionHandle<T>,2317        item_id: TokenId,2318        data: Vec<u8>2319    ) -> DispatchResult {2320        let collection_id = collection.id;2321        let mut item = <NftItemList<T>>::get(collection_id, item_id)2322            .ok_or(Error::<T>::TokenNotFound)?;2323        2324        item.variable_data = data;23252326        <NftItemList<T>>::insert(collection_id, item_id, item);2327        2328        Ok(())2329    }23302331    fn init_collection(item: &Collection<T>) {2332        // check params2333        assert!(2334            item.decimal_points <= MAX_DECIMAL_POINTS,2335            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2336        );2337        assert!(2338            item.name.len() <= 64,2339            "Collection name can not be longer than 63 char"2340        );2341        assert!(2342            item.name.len() <= 256,2343            "Collection description can not be longer than 255 char"2344        );2345        assert!(2346            item.token_prefix.len() <= 16,2347            "Token prefix can not be longer than 15 char"2348        );23492350        // Generate next collection ID2351        let next_id = CreatedCollectionCount::get()2352            .checked_add(1)2353            .unwrap();23542355        CreatedCollectionCount::put(next_id);2356    }23572358    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2359        let current_index = <ItemListIndex>::get(collection_id)2360            .checked_add(1)2361            .unwrap();23622363        let item_owner = item.owner.clone();2364        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();23652366        <ItemListIndex>::insert(collection_id, current_index);23672368        // Update balance2369        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2370            .checked_add(1)2371            .unwrap();2372        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2373    }23742375    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2376        let current_index = <ItemListIndex>::get(collection_id)2377            .checked_add(1)2378            .unwrap();23792380        Self::add_token_index(collection_id, current_index, owner).unwrap();23812382        <ItemListIndex>::insert(collection_id, current_index);23832384        // Update balance2385        let new_balance = <Balance<T>>::get(collection_id, owner)2386            .checked_add(item.value)2387            .unwrap();2388        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2389    }23902391    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2392        let current_index = <ItemListIndex>::get(collection_id)2393            .checked_add(1)2394            .unwrap();23952396        let value = item.owner.first().unwrap().fraction;2397        let owner = item.owner.first().unwrap().owner.clone();23982399        Self::add_token_index(collection_id, current_index, &owner).unwrap();24002401        <ItemListIndex>::insert(collection_id, current_index);24022403        // Update balance2404        let new_balance = <Balance<T>>::get(collection_id, &owner)2405            .checked_add(value)2406            .unwrap();2407        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2408    }24092410    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {2411        // add to account limit2412        if <AccountItemCount<T>>::contains_key(owner) {24132414            // bound Owned tokens by a single address2415            let count = <AccountItemCount<T>>::get(owner);2416            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);24172418            <AccountItemCount<T>>::insert(owner.clone(), count2419                .checked_add(1)2420                .ok_or(Error::<T>::NumOverflow)?);2421        }2422        else {2423            <AccountItemCount<T>>::insert(owner.clone(), 1);2424        }24252426        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2427        if list_exists {2428            let mut list = <AddressTokens<T>>::get(collection_id, owner);2429            let item_contains = list.contains(&item_index.clone());24302431            if !item_contains {2432                list.push(item_index.clone());2433            }24342435            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2436        } else {2437            let mut itm = Vec::new();2438            itm.push(item_index.clone());2439            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2440        }24412442        Ok(())2443    }24442445    fn remove_token_index(2446        collection_id: CollectionId,2447        item_index: TokenId,2448        owner: &T::AccountId,2449    ) -> DispatchResult {24502451        // update counter2452        <AccountItemCount<T>>::insert(owner.clone(), 2453            <AccountItemCount<T>>::get(owner)2454            .checked_sub(1)2455            .ok_or(Error::<T>::NumOverflow)?);245624572458        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2459        if list_exists {2460            let mut list = <AddressTokens<T>>::get(collection_id, owner);2461            let item_contains = list.contains(&item_index.clone());24622463            if item_contains {2464                list.retain(|&item| item != item_index);2465                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2466            }2467        }24682469        Ok(())2470    }24712472    fn move_token_index(2473        collection_id: CollectionId,2474        item_index: TokenId,2475        old_owner: &T::AccountId,2476        new_owner: &T::AccountId,2477    ) -> DispatchResult {2478        Self::remove_token_index(collection_id, item_index, old_owner)?;2479        Self::add_token_index(collection_id, item_index, new_owner)?;24802481        Ok(())2482    }2483    2484    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2485        ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);24862487        Ok(())2488    }2489}24902491////////////////////////////////////////////////////////////////////////////////////////////////////2492// Economic models2493// #region24942495/// Fee multiplier.2496pub type Multiplier = FixedU128;24972498type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;24992500/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2501/// in the queue.2502#[derive(Encode, Decode, Clone, Eq, PartialEq)]2503pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);25042505impl<T: Config + Send + Sync> sp_std::fmt::Debug 2506    for ChargeTransactionPayment<T>2507{2508	#[cfg(feature = "std")]2509	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2510		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2511	}2512	#[cfg(not(feature = "std"))]2513	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2514		Ok(())2515	}2516}25172518impl<T: Config> ChargeTransactionPayment<T>2519where2520    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2521    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2522    T::AccountId: AsRef<[u8]>,2523    T::AccountId: UncheckedFrom<T::Hash>,2524{2525    fn traditional_fee(2526        len: usize,2527        info: &DispatchInfoOf<T::Call>,2528        tip: BalanceOf<T>,2529    ) -> BalanceOf<T>2530    where2531        T::Call: Dispatchable<Info = DispatchInfo>,2532    {2533        <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2534    }25352536	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2537        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2538        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2539        let len_saturation = max_block_length as u64 / (len as u64).max(1);2540        let coefficient: BalanceOf<T> = weight_saturation2541            .min(len_saturation)2542            .saturated_into::<BalanceOf<T>>();2543        final_fee2544            .saturating_mul(coefficient)2545            .saturated_into::<TransactionPriority>()2546    }25472548    fn withdraw_fee(2549        &self,2550        who: &T::AccountId,2551        call: &T::Call,2552        info: &DispatchInfoOf<T::Call>,2553        len: usize,2554	) -> Result<2555		(2556			BalanceOf<T>,2557			<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2558		),2559		TransactionValidityError,2560	> {2561        let tip = self.0;25622563        let fee = Self::traditional_fee(len, info, tip);25642565        // Only mess with balances if fee is not zero.2566        if fee.is_zero() {2567            return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2568			.map(|i| (fee, i));2569        }25702571        // Determine who is paying transaction fee based on ecnomic model2572        // Parse call to extract collection ID and access collection sponsor2573        let mut sponsor: Option<T::AccountId> = (|| match IsSubType::<Call<T>>::is_sub_type(call) {2574            Some(Call::create_item(collection_id, _owner, _properties)) => {2575                let collection = <CollectionById<T>>::get(collection_id)?;25762577                // sponsor timeout2578                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;25792580                let limit = collection.limits.sponsor_transfer_timeout;2581                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2582                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2583                    let limit_time = last_tx_block + limit.into();2584                    if block_number <= limit_time {2585                        return None;2586                    }2587                }2588                <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);25892590                // check free create limit2591                if collection.limits.sponsored_data_size >= (_properties.len() as u32) {2592                    collection.sponsorship.sponsor()2593                        .cloned()2594                } else {2595                    None2596                }2597            }2598            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2599                let collection = <CollectionById<T>>::get(collection_id)?;2600                2601                let mut sponsor_transfer = false;2602                if collection.sponsorship.confirmed() {26032604                    let collection_limits = collection.limits;2605                    let collection_mode = collection.mode;2606    2607                    // sponsor timeout2608                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2609                    sponsor_transfer = match collection_mode {2610                        CollectionMode::NFT => {2611    2612                            // get correct limit2613                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2614                                collection_limits.sponsor_transfer_timeout2615                            } else {2616                                ChainLimit::get().nft_sponsor_transfer_timeout2617                            };2618    2619                            let mut sponsored = true;2620                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2621                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2622                                let limit_time = last_tx_block + limit.into();2623                                if block_number <= limit_time {2624                                    sponsored = false;2625                                }2626                            }2627                            if sponsored {2628                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2629                            }26302631                            sponsored2632                        }2633                        CollectionMode::Fungible(_) => {2634    2635                            // get correct limit2636                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2637                                collection_limits.sponsor_transfer_timeout2638                            } else {2639                                ChainLimit::get().fungible_sponsor_transfer_timeout2640                            };2641    2642                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2643                            let mut sponsored = true;2644                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2645                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2646                                let limit_time = last_tx_block + limit.into();2647                                if block_number <= limit_time {2648                                    sponsored = false;2649                                }2650                            }2651                            if sponsored {2652                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2653                            }26542655                            sponsored2656                        }2657                        CollectionMode::ReFungible => {2658    2659                            // get correct limit2660                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2661                                collection_limits.sponsor_transfer_timeout2662                            } else {2663                                ChainLimit::get().refungible_sponsor_transfer_timeout2664                            };2665    2666                            let mut sponsored = true;2667                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2668                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2669                                let limit_time = last_tx_block + limit.into();2670                                if block_number <= limit_time {2671                                    sponsored = false;2672                                }2673                            }2674                            if sponsored {2675                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2676                            }26772678                            sponsored2679                        }2680                        _ => {2681                            false2682                        },2683                    };2684                }26852686                if !sponsor_transfer {2687                    None2688                } else {2689                    collection.sponsorship.sponsor()2690                        .cloned()2691                }2692            }26932694            Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2695                let mut sponsor_metadata_changes = false;26962697                let collection = <CollectionById<T>>::get(collection_id)?;26982699                if2700                    collection.sponsorship.confirmed() &&2701                    // Can't sponsor fungible collection, this tx will be rejected2702                    // as invalid2703                    !matches!(collection.mode, CollectionMode::Fungible(_)) &&2704                    data.len() <= collection.limits.sponsored_data_size as usize2705                {2706                    if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2707                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;27082709                        if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2710                            .map(|last_block| block_number - last_block > rate_limit)2711                            .unwrap_or(true) 2712                        {2713                            sponsor_metadata_changes = true;2714                            <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2715                        }2716                    }2717                }27182719                if !sponsor_metadata_changes {2720                    None2721                } else {2722                    collection.sponsorship.sponsor().cloned()2723                }2724            }27252726            _ => None,2727        })();27282729        match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {2730            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {27312732                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());27332734                let owned_contract = <ContractOwner<T>>::get(called_contract.clone()).as_ref() == Some(who);2735                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone());2736                  2737                if !owned_contract && white_list_enabled {2738                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2739                        return Err(InvalidTransaction::Call.into());2740                    }2741                }2742            },2743            _ => {},2744        }27452746        // Sponsor smart contracts2747        sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {27482749            // On instantiation: set the contract owner2750            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {27512752                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2753                    &who,2754                    code_hash,2755                    salt,2756                );2757                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());27582759                None2760            },27612762            // On instantiation with code: set the contract owner2763            Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt))  => {27642765                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2766                    &who,2767                    &T::Hashing::hash(&_code),2768                    _salt,2769                );27702771                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());27722773                None2774            }27752776            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2777            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {27782779                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());27802781                let mut sponsor_transfer = false;2782                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2783                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2784                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2785                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2786                    let limit_time = last_tx_block + rate_limit;27872788                    if block_number >= limit_time {2789                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2790                        sponsor_transfer = true;2791                    }2792                } else {2793                    sponsor_transfer = false;2794                }2795               2796                if sponsor_transfer {2797                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2798                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2799                            return Some(called_contract);2800                        }2801                    }2802                }28032804                None2805            },28062807            _ => None,2808        });28092810        let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());28112812		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2813			.map(|i| (fee, i))2814    }2815}281628172818impl<T: Config + Send + Sync> SignedExtension2819    for ChargeTransactionPayment<T>2820where2821    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2822    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2823    T::AccountId: AsRef<[u8]>,2824    T::AccountId: UncheckedFrom<T::Hash>,2825{2826    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2827    type AccountId = T::AccountId;2828    type Call = T::Call;2829    type AdditionalSigned = ();2830    type Pre = (2831        // tip2832        BalanceOf<T>,2833        // who pays fee2834        Self::AccountId,2835		// imbalance resulting from withdrawing the fee2836		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2837    );2838    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2839        Ok(())2840    }28412842    fn validate(2843        &self,2844        who: &Self::AccountId,2845        call: &Self::Call,2846        info: &DispatchInfoOf<Self::Call>,2847        len: usize,2848    ) -> TransactionValidity {2849		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2850		Ok(ValidTransaction {2851			priority: Self::get_priority(len, info, fee),2852			..Default::default()2853		})2854    }28552856    fn pre_dispatch(2857        self,2858        who: &Self::AccountId,2859        call: &Self::Call,2860        info: &DispatchInfoOf<Self::Call>,2861        len: usize,2862    ) -> Result<Self::Pre, TransactionValidityError> {2863        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2864        Ok((self.0, who.clone(), imbalance))2865    }28662867    fn post_dispatch(2868        pre: Self::Pre,2869        info: &DispatchInfoOf<Self::Call>,2870        post_info: &PostDispatchInfoOf<Self::Call>,2871        len: usize,2872        _result: &DispatchResult,2873    ) -> Result<(), TransactionValidityError> {2874		let (tip, who, imbalance) = pre;2875		let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(2876			len as u32,2877			info,2878			post_info,2879			tip,2880		);2881		<T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2882		Ok(())2883    }2884}28852886// #endregion
after · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516pub use frame_support::{17    construct_runtime, decl_event, decl_module, decl_storage, decl_error,18    dispatch::DispatchResult,19    ensure, fail, parameter_types,20    traits::{21        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,22        Randomness, IsSubType, WithdrawReasons,23    },24    weights::{25        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},26        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,27        WeightToFeePolynomial, DispatchClass,28    },29    StorageValue,30    transactional,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use nft_data_structs::{36    MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,37	AccessMode, ChainLimits, Collection, CreateItemData, CollectionLimits,38    CollectionId, CollectionMode, CollectionHandle, TokenId, 39    SchemaVersion, SponsorshipState, Ownership,40    NftItemType, FungibleItemType, ReFungibleItemType41};4243#[cfg(test)]44mod mock;4546#[cfg(test)]47mod tests;4849mod default_weights;5051#[cfg(feature = "runtime-benchmarks")]52mod benchmarking;5354pub trait WeightInfo {55	fn create_collection() -> Weight;56	fn destroy_collection() -> Weight;57	fn add_to_white_list() -> Weight;58	fn remove_from_white_list() -> Weight;59    fn set_public_access_mode() -> Weight;60    fn set_mint_permission() -> Weight;61    fn change_collection_owner() -> Weight;62    fn add_collection_admin() -> Weight;63    fn remove_collection_admin() -> Weight;64    fn set_collection_sponsor() -> Weight;65    fn confirm_sponsorship() -> Weight;66    fn remove_collection_sponsor() -> Weight;67    fn create_item(s: usize) -> Weight;68    fn burn_item() -> Weight;69    fn transfer() -> Weight;70    fn approve() -> Weight;71    fn transfer_from() -> Weight;72    fn set_offchain_schema() -> Weight;73    fn set_const_on_chain_schema() -> Weight;74    fn set_variable_on_chain_schema() -> Weight;75    fn set_variable_meta_data() -> Weight;76    fn enable_contract_sponsoring() -> Weight;77    fn set_schema_version() -> Weight;78    fn set_chain_limits() -> Weight;79    fn set_contract_sponsoring_rate_limit() -> Weight;80    fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;81    fn toggle_contract_white_list() -> Weight;82    fn add_to_contract_white_list() -> Weight;83    fn remove_from_contract_white_list() -> Weight;84    fn set_collection_limits() -> Weight;85}8687decl_error! {88	/// Error for non-fungible-token module.89	pub enum Error for Module<T: Config> {90        /// Total collections bound exceeded.91        TotalCollectionsLimitExceeded,92		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.93        CollectionDecimalPointLimitExceeded, 94        /// Collection name can not be longer than 63 char.95        CollectionNameLimitExceeded, 96        /// Collection description can not be longer than 255 char.97        CollectionDescriptionLimitExceeded, 98        /// Token prefix can not be longer than 15 char.99        CollectionTokenPrefixLimitExceeded,100        /// This collection does not exist.101        CollectionNotFound,102        /// Item not exists.103        TokenNotFound,104        /// Admin not found105        AdminNotFound,106        /// Arithmetic calculation overflow.107        NumOverflow,       108        /// Account already has admin role.109        AlreadyAdmin,  110        /// You do not own this collection.111        NoPermission,112        /// This address is not set as sponsor, use setCollectionSponsor first.113        ConfirmUnsetSponsorFail,114        /// Collection is not in mint mode.115        PublicMintingNotAllowed,116        /// Sender parameter and item owner must be equal.117        MustBeTokenOwner,118        /// Item balance not enough.119        TokenValueTooLow,120        /// Size of item is too large.121        NftSizeLimitExceeded,122        /// No approve found123        ApproveNotFound,124        /// Requested value more than approved.125        TokenValueNotEnough,126        /// Only approved addresses can call this method.127        ApproveRequired,128        /// Address is not in white list.129        AddresNotInWhiteList,130        /// Number of collection admins bound exceeded.131        CollectionAdminsLimitExceeded,132        /// Owned tokens by a single address bound exceeded.133        AddressOwnershipLimitExceeded,134        /// Length of items properties must be greater than 0.135        EmptyArgument,136        /// const_data exceeded data limit.137        TokenConstDataLimitExceeded,138        /// variable_data exceeded data limit.139        TokenVariableDataLimitExceeded,140        /// Not NFT item data used to mint in NFT collection.141        NotNftDataUsedToMintNftCollectionToken,142        /// Not Fungible item data used to mint in Fungible collection.143        NotFungibleDataUsedToMintFungibleCollectionToken,144        /// Not Re Fungible item data used to mint in Re Fungible collection.145        NotReFungibleDataUsedToMintReFungibleCollectionToken,146        /// Unexpected collection type.147        UnexpectedCollectionType,148        /// Can't store metadata in fungible tokens.149        CantStoreMetadataInFungibleTokens,150        /// Collection token limit exceeded151        CollectionTokenLimitExceeded,152        /// Account token limit exceeded per collection153        AccountTokenLimitExceeded,154        /// Collection limit bounds per collection exceeded155        CollectionLimitBoundsExceeded,156        /// Tried to enable permissions which are only permitted to be disabled157        OwnerPermissionsCantBeReverted,158        /// Schema data size limit bound exceeded159        SchemaDataLimitExceeded,160        /// Maximum refungibility exceeded161        WrongRefungiblePieces,162        /// createRefungible should be called with one owner163        BadCreateRefungibleCall,164	}165}166167// + pallet_transaction_payment::Config  + pallet_nft_transaction_payment::Config + pallet_contracts::Config168169170pub trait Config: system::Config + Sized {171    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;172173    /// Weight information for extrinsics in this pallet.174	type WeightInfo: WeightInfo;175176    type Currency: Currency<Self::AccountId>;177    type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;178    type TreasuryAccountId: Get<Self::AccountId>;179}180181// # Used definitions182//183// ## User control levels184//185// chain-controlled - key is uncontrolled by user186//                    i.e autoincrementing index187//                    can use non-cryptographic hash188// real - key is controlled by user189//        but it is hard to generate enough colliding values, i.e owner of signed txs190//        can use non-cryptographic hash191// controlled - key is completly controlled by users192//              i.e maps with mutable keys193//              should use cryptographic hash194//195// ## User control level downgrade reasons196//197// ?1 - chain-controlled -> controlled198//      collections/tokens can be destroyed, resulting in massive holes199// ?2 - chain-controlled -> controlled200//      same as ?1, but can be only added, resulting in easier exploitation201// ?3 - real -> controlled202//      no confirmation required, so addresses can be easily generated203decl_storage! {204    trait Store for Module<T: Config> as Nft {205206        //#region Private members207        /// Id of next collection208        CreatedCollectionCount: u32;209        /// Used for migrations210        ChainVersion: u64;211        /// Id of last collection token212        /// Collection id (controlled?1)213        ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;214        //#endregion215216        //#region Chain limits struct217        pub ChainLimit get(fn chain_limit) config(): ChainLimits;218        //#endregion219220        //#region Bound counters221        /// Amount of collections destroyed, used for total amount tracking with222        /// CreatedCollectionCount223        DestroyedCollectionCount: u32;224        /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)225        /// Account id (real)226        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;227        //#endregion228229        //#region Basic collections230        /// Collection info231        /// Collection id (controlled?1)232        pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;233        /// List of collection admins234        /// Collection id (controlled?2)235        pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::AccountId>;236        /// Whitelisted collection users237        /// Collection id (controlled?2), user id (controlled?3)238        pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;239        //#endregion240241        /// How many of collection items user have242        /// Collection id (controlled?2), account id (real)243        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;244245        /// Amount of items which spender can transfer out of owners account (via transferFrom)246        /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))247        /// TODO: Off chain worker should remove from this map when token gets removed248        pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;249250        //#region Item collections251        /// Collection id (controlled?2), token id (controlled?1)252        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::AccountId>>;253        /// Collection id (controlled?2), owner (controlled?2)254        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;255        /// Collection id (controlled?2), token id (controlled?1)256        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::AccountId>>;257        //#endregion258259        //#region Index list260        /// Collection id (controlled?2), tokens owner (controlled?2)261        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;262        //#endregion263264        //#region Tokens transfer rate limit baskets265        /// (Collection id (controlled?2), who created (real))266        /// TODO: Off chain worker should remove from this map when collection gets removed267        pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;268        /// Collection id (controlled?2), token id (controlled?2)269        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;270        /// Collection id (controlled?2), owning user (real)271        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;272        /// Collection id (controlled?2), token id (controlled?2)273        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;274        //#endregion275276        /// Variable metadata sponsoring277        /// Collection id (controlled?2), token id (controlled?2)278        pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;279      280        //#region Contract Sponsorship and Ownership281        /// Contract address (real)282        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;283        /// Contract address (real)284        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;285        /// (Contract address(real), caller (real))286        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;287        /// Contract address (real)288        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;289        /// Contract address (real)290        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 291        /// Contract address (real) => Whitelisted user (controlled?3)292        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 293        //#endregion294    }295    add_extra_genesis {296        build(|config: &GenesisConfig<T>| {297            // Modification of storage298            for (_num, _c) in &config.collection_id {299                <Module<T>>::init_collection(_c);300            }301302            for (_num, _c, _i) in &config.nft_item_id {303                <Module<T>>::init_nft_token(*_c, _i);304            }305306            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {307                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);308            }309310            for (_num, _c, _i) in &config.refungible_item_id {311                <Module<T>>::init_refungible_token(*_c, _i);312            }313        })314    }315}316317decl_event!(318    pub enum Event<T>319    where320        AccountId = <T as system::Config>::AccountId,321    {322        /// New collection was created323        /// 324        /// # Arguments325        /// 326        /// * collection_id: Globally unique identifier of newly created collection.327        /// 328        /// * mode: [CollectionMode] converted into u8.329        /// 330        /// * account_id: Collection owner.331        CollectionCreated(CollectionId, u8, AccountId),332333        /// New item was created.334        /// 335        /// # Arguments336        /// 337        /// * collection_id: Id of the collection where item was created.338        /// 339        /// * item_id: Id of an item. Unique within the collection.340        ///341        /// * recipient: Owner of newly created item 342        ItemCreated(CollectionId, TokenId, AccountId),343344        /// Collection item was burned.345        /// 346        /// # Arguments347        /// 348        /// collection_id.349        /// 350        /// item_id: Identifier of burned NFT.351        ItemDestroyed(CollectionId, TokenId),352353        /// Item was transferred354        ///355        /// * collection_id: Id of collection to which item is belong356        ///357        /// * item_id: Id of an item358        ///359        /// * sender: Original owner of item360        ///361        /// * recipient: New owner of item362        ///363        /// * amount: Always 1 for NFT364        Transfer(CollectionId, TokenId, AccountId, AccountId, u128),365366        /// * collection_id367        ///368        /// * item_id369        ///370        /// * sender371        ///372        /// * spender373        ///374        /// * amount375        Approved(CollectionId, TokenId, AccountId, AccountId, u128),376    }377);378379decl_module! {380    pub struct Module<T: Config> for enum Call 381    where 382        origin: T::Origin383    {384        fn deposit_event() = default;385        type Error = Error<T>;386387        fn on_initialize(_now: T::BlockNumber) -> Weight {388            0389        }390391        /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.392        /// 393        /// # Permissions394        /// 395        /// * Anyone.396        /// 397        /// # Arguments398        /// 399        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.400        /// 401        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.402        /// 403        /// * token_prefix: UTF-8 string with token prefix.404        /// 405        /// * mode: [CollectionMode] collection type and type dependent data.406        // returns collection ID407        #[weight = <T as Config>::WeightInfo::create_collection()]408        #[transactional]409        pub fn create_collection(origin,410                                 collection_name: Vec<u16>,411                                 collection_description: Vec<u16>,412                                 token_prefix: Vec<u8>,413                                 mode: CollectionMode) -> DispatchResult {414415            // Anyone can create a collection416            let who = ensure_signed(origin)?;417418            // Take a (non-refundable) deposit of collection creation419            let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();420            imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(421                &T::TreasuryAccountId::get(),422                T::CollectionCreationPrice::get(),423            ));424            <T as Config>::Currency::settle(425                &who,426                imbalance,427                WithdrawReasons::TRANSFER,428                ExistenceRequirement::KeepAlive,429            ).map_err(|_| Error::<T>::NoPermission)?;430431            let decimal_points = match mode {432                CollectionMode::Fungible(points) => points,433                _ => 0434            };435436            let chain_limit = ChainLimit::get();437438            let created_count = CreatedCollectionCount::get();439            let destroyed_count = DestroyedCollectionCount::get();440441            // bound Total number of collections442            ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);443444            // check params445            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);446            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);447            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);448            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);449450            // Generate next collection ID451            let next_id = created_count452                .checked_add(1)453                .ok_or(Error::<T>::NumOverflow)?;454455            CreatedCollectionCount::put(next_id);456457            let limits = CollectionLimits {458                sponsored_data_size: chain_limit.custom_data_limit,459                ..Default::default()460            };461462            // Create new collection463            let new_collection = Collection {464                owner: who.clone(),465                name: collection_name,466                mode: mode.clone(),467                mint_mode: false,468                access: AccessMode::Normal,469                description: collection_description,470                decimal_points: decimal_points,471                token_prefix: token_prefix,472                offchain_schema: Vec::new(),473                schema_version: SchemaVersion::ImageURL,474                sponsorship: SponsorshipState::Disabled,475                variable_on_chain_schema: Vec::new(),476                const_on_chain_schema: Vec::new(),477                limits,478            };479480            // Add new collection to map481            <CollectionById<T>>::insert(next_id, new_collection);482483            // call event484            Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who.clone()));485486            Ok(())487        }488489        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.490        /// 491        /// # Permissions492        /// 493        /// * Collection Owner.494        /// 495        /// # Arguments496        /// 497        /// * collection_id: collection to destroy.498        #[weight = <T as Config>::WeightInfo::destroy_collection()]499        #[transactional]500        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {501502            let sender = ensure_signed(origin)?;503            let collection = Self::get_collection(collection_id)?;504            Self::check_owner_permissions(&collection, sender)?;505            if !collection.limits.owner_can_destroy {506                fail!(Error::<T>::NoPermission);507            }508509            <AddressTokens<T>>::remove_prefix(collection_id);510            <Allowances<T>>::remove_prefix(collection_id);511            <Balance<T>>::remove_prefix(collection_id);512            <ItemListIndex>::remove(collection_id);513            <AdminList<T>>::remove(collection_id);514            <CollectionById<T>>::remove(collection_id);515            <WhiteList<T>>::remove_prefix(collection_id);516517            <NftItemList<T>>::remove_prefix(collection_id);518            <FungibleItemList<T>>::remove_prefix(collection_id);519            <ReFungibleItemList<T>>::remove_prefix(collection_id);520521            <NftTransferBasket<T>>::remove_prefix(collection_id);522            <FungibleTransferBasket<T>>::remove_prefix(collection_id);523            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);524525            <VariableMetaDataBasket<T>>::remove_prefix(collection_id);526527            DestroyedCollectionCount::put(DestroyedCollectionCount::get()528                .checked_add(1)529                .ok_or(Error::<T>::NumOverflow)?);530531            Ok(())532        }533534        /// Add an address to white list.535        /// 536        /// # Permissions537        /// 538        /// * Collection Owner539        /// * Collection Admin540        /// 541        /// # Arguments542        /// 543        /// * collection_id.544        /// 545        /// * address.546        #[weight = <T as Config>::WeightInfo::add_to_white_list()]547        #[transactional]548        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{549550            let sender = ensure_signed(origin)?;551            let collection = Self::get_collection(collection_id)?;552            Self::check_owner_or_admin_permissions(&collection, sender)?;553554            <WhiteList<T>>::insert(collection_id, address, true);555            556            Ok(())557        }558559        /// Remove an address from white list.560        /// 561        /// # Permissions562        /// 563        /// * Collection Owner564        /// * Collection Admin565        /// 566        /// # Arguments567        /// 568        /// * collection_id.569        /// 570        /// * address.571        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]572        #[transactional]573        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{574575            let sender = ensure_signed(origin)?;576            let collection = Self::get_collection(collection_id)?;577            Self::check_owner_or_admin_permissions(&collection, sender)?;578579            <WhiteList<T>>::remove(collection_id, address);580581            Ok(())582        }583584        /// Toggle between normal and white list access for the methods with access for `Anyone`.585        /// 586        /// # Permissions587        /// 588        /// * Collection Owner.589        /// 590        /// # Arguments591        /// 592        /// * collection_id.593        /// 594        /// * mode: [AccessMode]595        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]596        #[transactional]597        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult598        {599            let sender = ensure_signed(origin)?;600601            let mut target_collection = Self::get_collection(collection_id)?;602            Self::check_owner_permissions(&target_collection, sender)?;603            target_collection.access = mode;604            Self::save_collection(target_collection);605606            Ok(())607        }608609        /// Allows Anyone to create tokens if:610        /// * White List is enabled, and611        /// * Address is added to white list, and612        /// * This method was called with True parameter613        /// 614        /// # Permissions615        /// * Collection Owner616        ///617        /// # Arguments618        /// 619        /// * collection_id.620        /// 621        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.622        #[weight = <T as Config>::WeightInfo::set_mint_permission()]623        #[transactional]624        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult625        {626            let sender = ensure_signed(origin)?;627628            let mut target_collection = Self::get_collection(collection_id)?;629            Self::check_owner_permissions(&target_collection, sender)?;630            target_collection.mint_mode = mint_permission;631            Self::save_collection(target_collection);632633            Ok(())634        }635636        /// Change the owner of the collection.637        /// 638        /// # Permissions639        /// 640        /// * Collection Owner.641        /// 642        /// # Arguments643        /// 644        /// * collection_id.645        /// 646        /// * new_owner.647        #[weight = <T as Config>::WeightInfo::change_collection_owner()]648        #[transactional]649        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {650651            let sender = ensure_signed(origin)?;652            let mut target_collection = Self::get_collection(collection_id)?;653            Self::check_owner_permissions(&target_collection, sender)?;654            target_collection.owner = new_owner;655            Self::save_collection(target_collection);656657            Ok(())658        }659660        /// Adds an admin of the Collection.661        /// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership. 662        /// 663        /// # Permissions664        /// 665        /// * Collection Owner.666        /// * Collection Admin.667        /// 668        /// # Arguments669        /// 670        /// * collection_id: ID of the Collection to add admin for.671        /// 672        /// * new_admin_id: Address of new admin to add.673        #[weight = <T as Config>::WeightInfo::add_collection_admin()]674        #[transactional]675        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {676677            let sender = ensure_signed(origin)?;678            let collection = Self::get_collection(collection_id)?;679            Self::check_owner_or_admin_permissions(&collection, sender)?;680            let mut admin_arr = <AdminList<T>>::get(collection_id);681682            match admin_arr.binary_search(&new_admin_id) {683                Ok(_) => {},684                Err(idx) => {685                    let limits = ChainLimit::get();686                    ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);687                    admin_arr.insert(idx, new_admin_id);688                    <AdminList<T>>::insert(collection_id, admin_arr);689                }690            }691            Ok(())692        }693694        /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.695        ///696        /// # Permissions697        /// 698        /// * Collection Owner.699        /// * Collection Admin.700        /// 701        /// # Arguments702        /// 703        /// * collection_id: ID of the Collection to remove admin for.704        /// 705        /// * account_id: Address of admin to remove.706        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]707        #[transactional]708        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {709710            let sender = ensure_signed(origin)?;711            let collection = Self::get_collection(collection_id)?;712            Self::check_owner_or_admin_permissions(&collection, sender)?;713            let mut admin_arr = <AdminList<T>>::get(collection_id);714715            match admin_arr.binary_search(&account_id) {716                Ok(idx) => {717                    admin_arr.remove(idx);718                    <AdminList<T>>::insert(collection_id, admin_arr);719                },720                Err(_) => {}721            }722            Ok(())723        }724725        /// # Permissions726        /// 727        /// * Collection Owner728        /// 729        /// # Arguments730        /// 731        /// * collection_id.732        /// 733        /// * new_sponsor.734        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]735        #[transactional]736        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {737738            let sender = ensure_signed(origin)?;739            let mut target_collection = Self::get_collection(collection_id)?;740            Self::check_owner_permissions(&target_collection, sender)?;741742            target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);743            Self::save_collection(target_collection);744745            Ok(())746        }747748        /// # Permissions749        /// 750        /// * Sponsor.751        /// 752        /// # Arguments753        /// 754        /// * collection_id.755        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]756        #[transactional]757        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {758759            let sender = ensure_signed(origin)?;760761            let mut target_collection = Self::get_collection(collection_id)?;762            ensure!(763                target_collection.sponsorship.pending_sponsor() == Some(&sender),764                Error::<T>::ConfirmUnsetSponsorFail765            );766767            target_collection.sponsorship = SponsorshipState::Confirmed(sender);768            Self::save_collection(target_collection);769770            Ok(())771        }772773        /// Switch back to pay-per-own-transaction model.774        ///775        /// # Permissions776        ///777        /// * Collection owner.778        /// 779        /// # Arguments780        /// 781        /// * collection_id.782        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]783        #[transactional]784        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {785786            let sender = ensure_signed(origin)?;787788            let mut target_collection = Self::get_collection(collection_id)?;789            Self::check_owner_permissions(&target_collection, sender)?;790791            target_collection.sponsorship = SponsorshipState::Disabled;792            Self::save_collection(target_collection);793794            Ok(())795        }796797        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.798        /// 799        /// # Permissions800        /// 801        /// * Collection Owner.802        /// * Collection Admin.803        /// * Anyone if804        ///     * White List is enabled, and805        ///     * Address is added to white list, and806        ///     * MintPermission is enabled (see SetMintPermission method)807        /// 808        /// # Arguments809        /// 810        /// * collection_id: ID of the collection.811        /// 812        /// * owner: Address, initial owner of the NFT.813        ///814        /// * data: Token data to store on chain.815        // #[weight =816        // (130_000_000 as Weight)817        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))818        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))819        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]820821        #[weight = <T as Config>::WeightInfo::create_item(data.len())]822        #[transactional]823        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {824            let sender = ensure_signed(origin)?;825            Self::create_item_internal(sender, collection_id, owner, data)826        }827828        /// This method creates multiple items in a collection created with CreateCollection method.829        /// 830        /// # Permissions831        /// 832        /// * Collection Owner.833        /// * Collection Admin.834        /// * Anyone if835        ///     * White List is enabled, and836        ///     * Address is added to white list, and837        ///     * MintPermission is enabled (see SetMintPermission method)838        /// 839        /// # Arguments840        /// 841        /// * collection_id: ID of the collection.842        /// 843        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].844        /// 845        /// * owner: Address, initial owner of the NFT.846        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()847                               .map(|data| { data.len() })848                               .sum())]849        #[transactional]850        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {851852            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);853            let sender = ensure_signed(origin)?;854855            let target_collection = Self::get_collection(collection_id)?;856857            Self::can_create_items_in_collection(&target_collection, &sender, &owner, items_data.len() as u32)?;858859            for data in &items_data {860                Self::validate_create_item_args(&target_collection, data)?;861            }862            for data in &items_data {863                Self::create_item_no_validation(&target_collection, owner.clone(), data.clone())?;864            }865866            Ok(())867        }868869        /// Destroys a concrete instance of NFT.870        /// 871        /// # Permissions872        /// 873        /// * Collection Owner.874        /// * Collection Admin.875        /// * Current NFT Owner.876        /// 877        /// # Arguments878        /// 879        /// * collection_id: ID of the collection.880        /// 881        /// * item_id: ID of NFT to burn.882        #[weight = <T as Config>::WeightInfo::burn_item()]883        #[transactional]884        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {885886            let sender = ensure_signed(origin)?;887888            // Transfer permissions check889            let target_collection = Self::get_collection(collection_id)?;890            ensure!(891                Self::is_item_owner(sender.clone(), &target_collection, item_id) ||892                (893                    target_collection.limits.owner_can_transfer &&894                    Self::is_owner_or_admin_permissions(&target_collection, sender.clone())895                ),896                Error::<T>::NoPermission897            );898899            if target_collection.access == AccessMode::WhiteList {900                Self::check_white_list(&target_collection, &sender)?;901            }902903            match target_collection.mode904            {905                CollectionMode::NFT => Self::burn_nft_item(&target_collection, item_id)?,906                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, &target_collection, value)?,907                CollectionMode::ReFungible  => Self::burn_refungible_item(&target_collection, item_id, &sender)?,908                _ => ()909            };910911            // call event912            Self::deposit_event(RawEvent::ItemDestroyed(target_collection.id, item_id));913914            Ok(())915        }916917        /// Change ownership of the token.918        /// 919        /// # Permissions920        /// 921        /// * Collection Owner922        /// * Collection Admin923        /// * Current NFT owner924        ///925        /// # Arguments926        /// 927        /// * recipient: Address of token recipient.928        /// 929        /// * collection_id.930        /// 931        /// * item_id: ID of the item932        ///     * Non-Fungible Mode: Required.933        ///     * Fungible Mode: Ignored.934        ///     * Re-Fungible Mode: Required.935        /// 936        /// * value: Amount to transfer.937        ///     * Non-Fungible Mode: Ignored938        ///     * Fungible Mode: Must specify transferred amount939        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)940        #[weight = <T as Config>::WeightInfo::transfer()]941        #[transactional]942        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {943            let sender = ensure_signed(origin)?;944            let collection = Self::get_collection(collection_id)?;945946            Self::transfer_internal(sender, recipient, &collection, item_id, value)947        }948949        /// Set, change, or remove approved address to transfer the ownership of the NFT.950        /// 951        /// # Permissions952        /// 953        /// * Collection Owner954        /// * Collection Admin955        /// * Current NFT owner956        /// 957        /// # Arguments958        /// 959        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).960        /// 961        /// * collection_id.962        /// 963        /// * item_id: ID of the item.964        #[weight = <T as Config>::WeightInfo::approve()]965        #[transactional]966        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {967968            let sender = ensure_signed(origin)?;969            let target_collection = Self::get_collection(collection_id)?;970971            Self::token_exists(&target_collection, item_id)?;972973            // Transfer permissions check974            let bypasses_limits = target_collection.limits.owner_can_transfer &&975                Self::is_owner_or_admin_permissions(976                    &target_collection,977                    sender.clone(),978                );979980            let allowance_limit = if bypasses_limits {981                None982            } else if let Some(amount) = Self::owned_amount(983                sender.clone(),984                &target_collection,985                item_id,986            ) {987                Some(amount)988            } else {989                fail!(Error::<T>::NoPermission);990            };991992            if target_collection.access == AccessMode::WhiteList {993                Self::check_white_list(&target_collection, &sender)?;994                Self::check_white_list(&target_collection, &spender)?;995            }996997            let allowance: u128 = amount998                .checked_add(<Allowances<T>>::get(collection_id, (item_id, &sender, &spender)))999                .ok_or(Error::<T>::NumOverflow)?;1000            if let Some(limit) = allowance_limit {1001                ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1002            }1003            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);10041005            Self::deposit_event(RawEvent::Approved(target_collection.id, item_id, sender, spender, allowance));1006            Ok(())1007        }1008        1009        /// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.1010        /// 1011        /// # Permissions1012        /// * Collection Owner1013        /// * Collection Admin1014        /// * Current NFT owner1015        /// * Address approved by current NFT owner1016        /// 1017        /// # Arguments1018        /// 1019        /// * from: Address that owns token.1020        /// 1021        /// * recipient: Address of token recipient.1022        /// 1023        /// * collection_id.1024        /// 1025        /// * item_id: ID of the item.1026        /// 1027        /// * value: Amount to transfer.1028        #[weight = <T as Config>::WeightInfo::transfer_from()]1029        #[transactional]1030        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {10311032            let sender = ensure_signed(origin)?;1033            let target_collection = Self::get_collection(collection_id)?;10341035            // Check approval1036            let approval: u128 = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));10371038            // Limits check1039            Self::is_correct_transfer(&target_collection, &recipient)?;10401041            // Transfer permissions check         1042            ensure!(1043                approval >= value || 1044                (1045                    target_collection.limits.owner_can_transfer &&1046                    Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1047                ),1048                Error::<T>::NoPermission1049            );10501051            if target_collection.access == AccessMode::WhiteList {1052                Self::check_white_list(&target_collection, &sender)?;1053                Self::check_white_list(&target_collection, &recipient)?;1054            }10551056            // Reduce approval by transferred amount or remove if remaining approval drops to 01057            if approval.saturating_sub(value) > 0 {1058                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1059            }1060            else {1061                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1062            }10631064            match target_collection.mode1065            {1066                CollectionMode::NFT => Self::transfer_nft(&target_collection, item_id, from.clone(), recipient.clone())?,1067                CollectionMode::Fungible(_)  => Self::transfer_fungible(&target_collection, value, &from, &recipient)?,1068                CollectionMode::ReFungible  => Self::transfer_refungible(&target_collection, item_id, value, from.clone(), recipient.clone())?,1069                _ => ()1070            };10711072            Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, from, recipient, value));1073            Ok(())1074        }10751076        // #[weight = 0]1077        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {10781079        //     // let no_perm_mes = "You do not have permissions to modify this collection";1080        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1081        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1082        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);10831084        //     // // on_nft_received  call10851086        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;10871088        //     Ok(())1089        // }10901091        /// Set off-chain data schema.1092        /// 1093        /// # Permissions1094        /// 1095        /// * Collection Owner1096        /// * Collection Admin1097        /// 1098        /// # Arguments1099        /// 1100        /// * collection_id.1101        /// 1102        /// * schema: String representing the offchain data schema.1103        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1104        #[transactional]1105        pub fn set_variable_meta_data (1106            origin,1107            collection_id: CollectionId,1108            item_id: TokenId,1109            data: Vec<u8>1110        ) -> DispatchResult {1111            let sender = ensure_signed(origin)?;1112            1113            let target_collection = Self::get_collection(collection_id)?;1114            Self::token_exists(&target_collection, item_id)?;11151116            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);11171118            // Modify permissions check1119            ensure!(Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1120                Self::is_owner_or_admin_permissions(&target_collection, sender.clone()),1121                Error::<T>::NoPermission);11221123            match target_collection.mode1124            {1125                CollectionMode::NFT => Self::set_nft_variable_data(&target_collection, item_id, data)?,1126                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(&target_collection, item_id, data)?,1127                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1128                _ => fail!(Error::<T>::UnexpectedCollectionType)1129            };11301131            Ok(())1132        }1133 1134        /// Set schema standard1135        /// ImageURL1136        /// Unique1137        /// 1138        /// # Permissions1139        /// 1140        /// * Collection Owner1141        /// * Collection Admin1142        /// 1143        /// # Arguments1144        /// 1145        /// * collection_id.1146        /// 1147        /// * schema: SchemaVersion: enum1148        #[weight = <T as Config>::WeightInfo::set_schema_version()]1149        #[transactional]1150        pub fn set_schema_version(1151            origin,1152            collection_id: CollectionId,1153            version: SchemaVersion1154        ) -> DispatchResult {1155            let sender = ensure_signed(origin)?;1156            let mut target_collection = Self::get_collection(collection_id)?;1157            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;1158            target_collection.schema_version = version;1159            Self::save_collection(target_collection);11601161            Ok(())1162        }11631164        /// Set off-chain data schema.1165        /// 1166        /// # Permissions1167        /// 1168        /// * Collection Owner1169        /// * Collection Admin1170        /// 1171        /// # Arguments1172        /// 1173        /// * collection_id.1174        /// 1175        /// * schema: String representing the offchain data schema.1176        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1177        #[transactional]1178        pub fn set_offchain_schema(1179            origin,1180            collection_id: CollectionId,1181            schema: Vec<u8>1182        ) -> DispatchResult {1183            let sender = ensure_signed(origin)?;1184            let mut target_collection = Self::get_collection(collection_id)?;1185            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;11861187            // check schema limit1188            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");11891190            target_collection.offchain_schema = schema;1191            Self::save_collection(target_collection);11921193            Ok(())1194        }11951196        /// Set const on-chain data schema.1197        /// 1198        /// # Permissions1199        /// 1200        /// * Collection Owner1201        /// * Collection Admin1202        /// 1203        /// # Arguments1204        /// 1205        /// * collection_id.1206        /// 1207        /// * schema: String representing the const on-chain data schema.1208        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1209        #[transactional]1210        pub fn set_const_on_chain_schema (1211            origin,1212            collection_id: CollectionId,1213            schema: Vec<u8>1214        ) -> DispatchResult {1215            let sender = ensure_signed(origin)?;1216            let mut target_collection = Self::get_collection(collection_id)?;1217            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;12181219            // check schema limit1220            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");12211222            target_collection.const_on_chain_schema = schema;1223            Self::save_collection(target_collection);12241225            Ok(())1226        }12271228        /// Set variable on-chain data schema.1229        /// 1230        /// # Permissions1231        /// 1232        /// * Collection Owner1233        /// * Collection Admin1234        /// 1235        /// # Arguments1236        /// 1237        /// * collection_id.1238        /// 1239        /// * schema: String representing the variable on-chain data schema.1240        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1241        #[transactional]1242        pub fn set_variable_on_chain_schema (1243            origin,1244            collection_id: CollectionId,1245            schema: Vec<u8>1246        ) -> DispatchResult {1247            let sender = ensure_signed(origin)?;1248            let mut target_collection = Self::get_collection(collection_id)?;1249            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;12501251            // check schema limit1252            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");12531254            target_collection.variable_on_chain_schema = schema;1255            Self::save_collection(target_collection);12561257            Ok(())1258        }12591260        // Sudo permissions function1261        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1262        #[transactional]1263        pub fn set_chain_limits(1264            origin,1265            limits: ChainLimits1266        ) -> DispatchResult {12671268            #[cfg(not(feature = "runtime-benchmarks"))]1269            ensure_root(origin)?;12701271            <ChainLimit>::put(limits);1272            Ok(())1273        }12741275        /// Enable smart contract self-sponsoring.1276        /// 1277        /// # Permissions1278        /// 1279        /// * Contract Owner1280        /// 1281        /// # Arguments1282        /// 1283        /// * contract address1284        /// * enable flag1285        /// 1286        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1287        #[transactional]1288        pub fn enable_contract_sponsoring(1289            origin,1290            contract_address: T::AccountId,1291            enable: bool1292        ) -> DispatchResult {12931294            let sender = ensure_signed(origin)?;12951296            #[cfg(feature = "runtime-benchmarks")]1297            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());12981299            Self::ensure_contract_owned(sender, &contract_address)?;13001301            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1302            Ok(())1303        }13041305        /// Set the rate limit for contract sponsoring to specified number of blocks.1306        /// 1307        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1308        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1309        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1310        /// from contract endowment if there are at least B blocks between such transactions. 1311        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1312        /// 1313        /// # Permissions1314        /// 1315        /// * Contract Owner1316        /// 1317        /// # Arguments1318        /// 1319        /// -`contract_address`: Address of the contract to sponsor1320        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1321        /// 1322        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1323        #[transactional]1324        pub fn set_contract_sponsoring_rate_limit(1325            origin,1326            contract_address: T::AccountId,1327            rate_limit: T::BlockNumber1328        ) -> DispatchResult {1329            let sender = ensure_signed(origin)?;13301331            #[cfg(feature = "runtime-benchmarks")]1332            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13331334            Self::ensure_contract_owned(sender, &contract_address)?;1335            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1336            Ok(())1337        }13381339        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1340        /// 1341        /// # Permissions1342        /// 1343        /// * Address that deployed smart contract.1344        /// 1345        /// # Arguments1346        /// 1347        /// -`contract_address`: Address of the contract.1348        /// 1349        /// - `enable`: .  1350        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1351        #[transactional]1352        pub fn toggle_contract_white_list(1353            origin,1354            contract_address: T::AccountId,1355            enable: bool1356        ) -> DispatchResult {1357            let sender = ensure_signed(origin)?;13581359            #[cfg(feature = "runtime-benchmarks")]1360            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13611362            Self::ensure_contract_owned(sender, &contract_address)?;1363            if enable {1364                <ContractWhiteListEnabled<T>>::insert(contract_address, true);1365            } else {1366                <ContractWhiteListEnabled<T>>::remove(contract_address);1367            }1368            Ok(())1369        }1370        1371        /// Add an address to smart contract white list.1372        /// 1373        /// # Permissions1374        /// 1375        /// * Address that deployed smart contract.1376        /// 1377        /// # Arguments1378        /// 1379        /// -`contract_address`: Address of the contract.1380        ///1381        /// -`account_address`: Address to add.1382        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1383        #[transactional]1384        pub fn add_to_contract_white_list(1385            origin,1386            contract_address: T::AccountId,1387            account_address: T::AccountId1388        ) -> DispatchResult {1389            let sender = ensure_signed(origin)?;13901391            #[cfg(feature = "runtime-benchmarks")]1392            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1393            1394            Self::ensure_contract_owned(sender, &contract_address)?;      1395            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1396            Ok(())1397        }13981399        /// Remove an address from smart contract white list.1400        /// 1401        /// # Permissions1402        /// 1403        /// * Address that deployed smart contract.1404        /// 1405        /// # Arguments1406        /// 1407        /// -`contract_address`: Address of the contract.1408        ///1409        /// -`account_address`: Address to remove.1410        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1411        #[transactional]1412        pub fn remove_from_contract_white_list(1413            origin,1414            contract_address: T::AccountId,1415            account_address: T::AccountId1416        ) -> DispatchResult {1417            let sender = ensure_signed(origin)?;14181419            #[cfg(feature = "runtime-benchmarks")]1420            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14211422            Self::ensure_contract_owned(sender, &contract_address)?;1423            <ContractWhiteList<T>>::remove(contract_address, account_address);1424            Ok(())1425        }14261427        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1428        #[transactional]1429        pub fn set_collection_limits(1430            origin,1431            collection_id: u32,1432            new_limits: CollectionLimits<T::BlockNumber>,1433        ) -> DispatchResult {1434            let sender = ensure_signed(origin)?;1435            let mut target_collection = Self::get_collection(collection_id)?;1436            Self::check_owner_permissions(&target_collection, sender.clone())?;1437            let old_limits = &target_collection.limits;1438            let chain_limits = ChainLimit::get();14391440            // collection bounds1441            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1442                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1443                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1444                Error::<T>::CollectionLimitBoundsExceeded);14451446            // token_limit   check  prev1447            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1448            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);14491450            ensure!(1451                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1452                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1453                Error::<T>::OwnerPermissionsCantBeReverted,1454            );14551456            target_collection.limits = new_limits;1457            Self::save_collection(target_collection);14581459            Ok(())1460        } 1461    }1462}14631464impl<T: Config> Module<T> {1465    pub fn create_item_internal(sender: T::AccountId, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1466        let target_collection = Self::get_collection(collection_id)?;14671468        Self::can_create_items_in_collection(&target_collection, &sender, &owner, 1)?;1469        Self::validate_create_item_args(&target_collection, &data)?;1470        Self::create_item_no_validation(&target_collection, owner, data)?;14711472        Ok(())1473    }14741475    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1476        // Limits check1477        Self::is_correct_transfer(target_collection, &recipient)?;14781479        // Transfer permissions check1480        ensure!(Self::is_item_owner(sender.clone(), target_collection, item_id) ||1481            Self::is_owner_or_admin_permissions(target_collection, sender.clone()),1482            Error::<T>::NoPermission);14831484        if target_collection.access == AccessMode::WhiteList {1485            Self::check_white_list(target_collection, &sender)?;1486            Self::check_white_list(target_collection, &recipient)?;1487        }14881489        match target_collection.mode1490        {1491            CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1492            CollectionMode::Fungible(_)  => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1493            CollectionMode::ReFungible  => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1494            _ => ()1495        };14961497        Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender, recipient, value));14981499        Ok(())1500    }150115021503    fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::AccountId) -> DispatchResult {1504        let collection_id = collection.id;15051506        // check token limit and account token limit1507        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1508        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1509        1510        Ok(())1511    }15121513    fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::AccountId, owner: &T::AccountId, amount: u32) -> DispatchResult {1514        let collection_id = collection.id;15151516        // check token limit and account token limit1517        let total_items: u32 = ItemListIndex::get(collection_id)1518            .checked_add(amount)1519            .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1520        let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner).len() as u32)1521            .checked_add(amount)1522            .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1523        ensure!(collection.limits.token_limit >= total_items,  Error::<T>::CollectionTokenLimitExceeded);1524        ensure!(collection.limits.account_token_ownership_limit >= account_items,  Error::<T>::AccountTokenLimitExceeded);15251526        if !Self::is_owner_or_admin_permissions(collection, sender.clone()) {1527            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1528            Self::check_white_list(collection, owner)?;1529            Self::check_white_list(collection, sender)?;1530        }15311532        Ok(())1533    }15341535    fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1536        match target_collection.mode1537        {1538            CollectionMode::NFT => {1539                if let CreateItemData::NFT(data) = data {1540                    // check sizes1541                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1542                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1543                } else {1544                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1545                }1546            },1547            CollectionMode::Fungible(_) => {1548                if let CreateItemData::Fungible(_) = data {1549                } else {1550                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1551                }1552            },1553            CollectionMode::ReFungible => {1554                if let CreateItemData::ReFungible(data) = data {15551556                    // check sizes1557                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1558                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);15591560                    // Check refungibility limits1561                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1562                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1563                } else {1564                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1565                }1566            },1567            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1568        };15691570        Ok(())1571    }15721573    fn create_item_no_validation(collection: &CollectionHandle<T>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1574        match data1575        {1576            CreateItemData::NFT(data) => {1577                let item = NftItemType {1578                    owner: owner.clone(),1579                    const_data: data.const_data,1580                    variable_data: data.variable_data1581                };15821583                Self::add_nft_item(collection, item)?;1584            },1585            CreateItemData::Fungible(data) => {1586                Self::add_fungible_item(collection, &owner, data.value)?;1587            },1588            CreateItemData::ReFungible(data) => {1589                let mut owner_list = Vec::new();1590                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});15911592                let item = ReFungibleItemType {1593                    owner: owner_list,1594                    const_data: data.const_data,1595                    variable_data: data.variable_data1596                };15971598                Self::add_refungible_item(collection, item)?;1599            }1600        };16011602        Ok(())1603    }16041605    fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::AccountId, value: u128) -> DispatchResult {1606        let collection_id = collection.id;16071608        // Does new owner already have an account?1609        let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner).value;16101611        // Mint 1612        let item = FungibleItemType {1613            value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1614        };1615        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16161617        // Update balance1618        let new_balance = <Balance<T>>::get(collection_id, owner)1619            .checked_add(value)1620            .ok_or(Error::<T>::NumOverflow)?;1621        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);16221623        Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1624        Ok(())1625    }16261627    fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1628        let collection_id = collection.id;16291630        let current_index = <ItemListIndex>::get(collection_id)1631            .checked_add(1)1632            .ok_or(Error::<T>::NumOverflow)?;1633        let itemcopy = item.clone();16341635        ensure!(1636            item.owner.len() == 1,1637            Error::<T>::BadCreateRefungibleCall,1638        );1639        let item_owner = item.owner.first().expect("only one owner is defined");16401641        let value = item_owner.fraction;1642        let owner = item_owner.owner.clone();16431644        Self::add_token_index(collection_id, current_index, &owner)?;16451646        <ItemListIndex>::insert(collection_id, current_index);1647        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);16481649        // Update balance1650        let new_balance = <Balance<T>>::get(collection_id, &owner)1651            .checked_add(value)1652            .ok_or(Error::<T>::NumOverflow)?;1653        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);16541655        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1656        Ok(())1657    }16581659    fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::AccountId>) -> DispatchResult {1660        let collection_id = collection.id;16611662        let current_index = <ItemListIndex>::get(collection_id)1663            .checked_add(1)1664            .ok_or(Error::<T>::NumOverflow)?;16651666        let item_owner = item.owner.clone();1667        Self::add_token_index(collection_id, current_index, &item.owner)?;16681669        <ItemListIndex>::insert(collection_id, current_index);1670        <NftItemList<T>>::insert(collection_id, current_index, item);16711672        // Update balance1673        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1674            .checked_add(1)1675            .ok_or(Error::<T>::NumOverflow)?;1676        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);16771678        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));1679        Ok(())1680    }16811682    fn burn_refungible_item(1683        collection: &CollectionHandle<T>,1684        item_id: TokenId,1685        owner: &T::AccountId,1686    ) -> DispatchResult {1687        let collection_id = collection.id;16881689        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1690            .ok_or(Error::<T>::TokenNotFound)?;1691        let rft_balance = token1692            .owner1693            .iter()1694            .find(|&i| i.owner == *owner)1695            .ok_or(Error::<T>::TokenNotFound)?;1696        Self::remove_token_index(collection_id, item_id, owner)?;16971698        // update balance1699        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1700            .checked_sub(rft_balance.fraction)1701            .ok_or(Error::<T>::NumOverflow)?;1702        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);17031704        // Re-create owners list with sender removed1705        let index = token1706            .owner1707            .iter()1708            .position(|i| i.owner == *owner)1709            .expect("owned item is exists");1710        token.owner.remove(index);1711        let owner_count = token.owner.len();17121713        // Burn the token completely if this was the last (only) owner1714        if owner_count == 0 {1715            <ReFungibleItemList<T>>::remove(collection_id, item_id);1716            <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1717        }1718        else {1719            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1720        }17211722        Ok(())1723    }17241725    fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1726        let collection_id = collection.id;17271728        let item = <NftItemList<T>>::get(collection_id, item_id)1729            .ok_or(Error::<T>::TokenNotFound)?;1730        Self::remove_token_index(collection_id, item_id, &item.owner)?;17311732        // update balance1733        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1734            .checked_sub(1)1735            .ok_or(Error::<T>::NumOverflow)?;1736        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1737        <NftItemList<T>>::remove(collection_id, item_id);1738        <VariableMetaDataBasket<T>>::remove(collection_id, item_id);17391740        Ok(())1741    }17421743    fn burn_fungible_item(owner: &T::AccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {1744        let collection_id = collection.id;17451746        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1747        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);17481749        // update balance1750        let new_balance = <Balance<T>>::get(collection_id, owner)1751            .checked_sub(value)1752            .ok_or(Error::<T>::NumOverflow)?;1753        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17541755        if balance.value - value > 0 {1756            balance.value -= value;1757            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1758        }1759        else {1760            <FungibleItemList<T>>::remove(collection_id, owner);1761        }17621763        Ok(())1764    }17651766    pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1767        Ok(<CollectionById<T>>::get(collection_id)1768            .map(|collection| CollectionHandle {1769                id: collection_id,1770                collection1771            })1772            .ok_or(Error::<T>::CollectionNotFound)?)1773    }17741775    fn save_collection(collection: CollectionHandle<T>) {1776        <CollectionById<T>>::insert(collection.id, collection.collection);1777    }17781779    fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: T::AccountId) -> DispatchResult {1780        ensure!(1781            subject == target_collection.owner,1782            Error::<T>::NoPermission1783        );17841785        Ok(())1786    }17871788    fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: T::AccountId) -> bool {1789        subject == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)1790    }17911792    fn check_owner_or_admin_permissions(1793        collection: &CollectionHandle<T>,1794        subject: T::AccountId,1795    ) -> DispatchResult {1796        ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);17971798        Ok(())1799    }18001801    fn owned_amount(1802        subject: T::AccountId,1803        target_collection: &CollectionHandle<T>,1804        item_id: TokenId,1805    ) -> Option<u128> {1806        let collection_id = target_collection.id;18071808        match target_collection.mode {1809            CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == subject)1810                .then(|| 1),1811            CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject)1812                .value),1813            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1814                .owner1815                .iter()1816                .find(|i| i.owner == subject)1817                .map(|i| i.fraction),1818            CollectionMode::Invalid => None,1819        }1820    }18211822    fn is_item_owner(subject: T::AccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {1823        match target_collection.mode {1824            CollectionMode::Fungible(_) => true,1825            _ => Self::owned_amount(subject, target_collection, item_id).is_some(),1826        }1827    }18281829    fn check_white_list(collection: &CollectionHandle<T>, address: &T::AccountId) -> DispatchResult {1830        let collection_id = collection.id;18311832        let mes = Error::<T>::AddresNotInWhiteList;1833        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);18341835        Ok(())1836    }18371838    /// Check if token exists. In case of Fungible, check if there is an entry for 1839    /// the owner in fungible balances double map1840    fn token_exists(1841        target_collection: &CollectionHandle<T>,1842        item_id: TokenId,1843    ) -> DispatchResult {1844        let collection_id = target_collection.id;1845        let exists = match target_collection.mode1846        {1847            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1848            CollectionMode::Fungible(_)  => true,1849            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1850            _ => false1851        };18521853        ensure!(exists == true, Error::<T>::TokenNotFound);1854        Ok(())1855    }18561857    fn transfer_fungible(1858        collection: &CollectionHandle<T>,1859        value: u128,1860        owner: &T::AccountId,1861        recipient: &T::AccountId,1862    ) -> DispatchResult {1863        let collection_id = collection.id;18641865        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1866        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);18671868        // Send balance to recipient (updates balanceOf of recipient)1869        Self::add_fungible_item(collection, recipient, value)?;18701871        // update balanceOf of sender1872        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);18731874        // Reduce or remove sender1875        if balance.value == value {1876            <FungibleItemList<T>>::remove(collection_id, owner);1877        }1878        else {1879            balance.value -= value;1880            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1881        }18821883        Ok(())1884    }18851886    fn transfer_refungible(1887        collection: &CollectionHandle<T>,1888        item_id: TokenId,1889        value: u128,1890        owner: T::AccountId,1891        new_owner: T::AccountId,1892    ) -> DispatchResult {1893        let collection_id = collection.id;1894        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)1895            .ok_or(Error::<T>::TokenNotFound)?;18961897        let item = full_item1898            .owner1899            .iter()1900            .filter(|i| i.owner == owner)1901            .next()1902            .ok_or(Error::<T>::TokenNotFound)?;1903        let amount = item.fraction;19041905        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19061907        // update balance1908        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1909            .checked_sub(value)1910            .ok_or(Error::<T>::NumOverflow)?;1911        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19121913        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1914            .checked_add(value)1915            .ok_or(Error::<T>::NumOverflow)?;1916        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19171918        let old_owner = item.owner.clone();1919        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19201921        // transfer1922        if amount == value && !new_owner_has_account {1923            // change owner1924            // new owner do not have account1925            let mut new_full_item = full_item.clone();1926            new_full_item1927                .owner1928                .iter_mut()1929                .find(|i| i.owner == owner)1930                .expect("old owner does present in refungible")1931                .owner = new_owner.clone();1932            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19331934            // update index collection1935            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;1936        } else {1937            let mut new_full_item = full_item.clone();1938            new_full_item1939                .owner1940                .iter_mut()1941                .find(|i| i.owner == owner)1942                .expect("old owner does present in refungible")1943                .fraction -= value;19441945            // separate amount1946            if new_owner_has_account {1947                // new owner has account1948                new_full_item1949                    .owner1950                    .iter_mut()1951                    .find(|i| i.owner == new_owner)1952                    .expect("new owner has account")1953                    .fraction += value;1954            } else {1955                // new owner do not have account1956                new_full_item.owner.push(Ownership {1957                    owner: new_owner.clone(),1958                    fraction: value,1959                });1960                Self::add_token_index(collection_id, item_id, &new_owner)?;1961            }19621963            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1964        }19651966        Ok(())1967    }19681969    fn transfer_nft(1970        collection: &CollectionHandle<T>,1971        item_id: TokenId,1972        sender: T::AccountId,1973        new_owner: T::AccountId,1974    ) -> DispatchResult {1975        let collection_id = collection.id;1976        let mut item = <NftItemList<T>>::get(collection_id, item_id)1977            .ok_or(Error::<T>::TokenNotFound)?;19781979        ensure!(1980            sender == item.owner,1981            Error::<T>::MustBeTokenOwner1982        );19831984        // update balance1985        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1986            .checked_sub(1)1987            .ok_or(Error::<T>::NumOverflow)?;1988        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19891990        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1991            .checked_add(1)1992            .ok_or(Error::<T>::NumOverflow)?;1993        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19941995        // change owner1996        let old_owner = item.owner.clone();1997        item.owner = new_owner.clone();1998        <NftItemList<T>>::insert(collection_id, item_id, item);19992000        // update index collection2001        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;20022003        Ok(())2004    }2005    2006    fn set_re_fungible_variable_data(2007        collection: &CollectionHandle<T>,2008        item_id: TokenId,2009        data: Vec<u8>2010    ) -> DispatchResult {2011        let collection_id = collection.id;2012        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2013            .ok_or(Error::<T>::TokenNotFound)?;20142015        item.variable_data = data;20162017        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20182019        Ok(())2020    }20212022    fn set_nft_variable_data(2023        collection: &CollectionHandle<T>,2024        item_id: TokenId,2025        data: Vec<u8>2026    ) -> DispatchResult {2027        let collection_id = collection.id;2028        let mut item = <NftItemList<T>>::get(collection_id, item_id)2029            .ok_or(Error::<T>::TokenNotFound)?;2030        2031        item.variable_data = data;20322033        <NftItemList<T>>::insert(collection_id, item_id, item);2034        2035        Ok(())2036    }20372038    fn init_collection(item: &Collection<T>) {2039        // check params2040        assert!(2041            item.decimal_points <= MAX_DECIMAL_POINTS,2042            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2043        );2044        assert!(2045            item.name.len() <= 64,2046            "Collection name can not be longer than 63 char"2047        );2048        assert!(2049            item.name.len() <= 256,2050            "Collection description can not be longer than 255 char"2051        );2052        assert!(2053            item.token_prefix.len() <= 16,2054            "Token prefix can not be longer than 15 char"2055        );20562057        // Generate next collection ID2058        let next_id = CreatedCollectionCount::get()2059            .checked_add(1)2060            .unwrap();20612062        CreatedCollectionCount::put(next_id);2063    }20642065    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2066        let current_index = <ItemListIndex>::get(collection_id)2067            .checked_add(1)2068            .unwrap();20692070        let item_owner = item.owner.clone();2071        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();20722073        <ItemListIndex>::insert(collection_id, current_index);20742075        // Update balance2076        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2077            .checked_add(1)2078            .unwrap();2079        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2080    }20812082    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2083        let current_index = <ItemListIndex>::get(collection_id)2084            .checked_add(1)2085            .unwrap();20862087        Self::add_token_index(collection_id, current_index, owner).unwrap();20882089        <ItemListIndex>::insert(collection_id, current_index);20902091        // Update balance2092        let new_balance = <Balance<T>>::get(collection_id, owner)2093            .checked_add(item.value)2094            .unwrap();2095        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2096    }20972098    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2099        let current_index = <ItemListIndex>::get(collection_id)2100            .checked_add(1)2101            .unwrap();21022103        let value = item.owner.first().unwrap().fraction;2104        let owner = item.owner.first().unwrap().owner.clone();21052106        Self::add_token_index(collection_id, current_index, &owner).unwrap();21072108        <ItemListIndex>::insert(collection_id, current_index);21092110        // Update balance2111        let new_balance = <Balance<T>>::get(collection_id, &owner)2112            .checked_add(value)2113            .unwrap();2114        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2115    }21162117    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {2118        // add to account limit2119        if <AccountItemCount<T>>::contains_key(owner) {21202121            // bound Owned tokens by a single address2122            let count = <AccountItemCount<T>>::get(owner);2123            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21242125            <AccountItemCount<T>>::insert(owner.clone(), count2126                .checked_add(1)2127                .ok_or(Error::<T>::NumOverflow)?);2128        }2129        else {2130            <AccountItemCount<T>>::insert(owner.clone(), 1);2131        }21322133        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2134        if list_exists {2135            let mut list = <AddressTokens<T>>::get(collection_id, owner);2136            let item_contains = list.contains(&item_index.clone());21372138            if !item_contains {2139                list.push(item_index.clone());2140            }21412142            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2143        } else {2144            let mut itm = Vec::new();2145            itm.push(item_index.clone());2146            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2147        }21482149        Ok(())2150    }21512152    fn remove_token_index(2153        collection_id: CollectionId,2154        item_index: TokenId,2155        owner: &T::AccountId,2156    ) -> DispatchResult {21572158        // update counter2159        <AccountItemCount<T>>::insert(owner.clone(), 2160            <AccountItemCount<T>>::get(owner)2161            .checked_sub(1)2162            .ok_or(Error::<T>::NumOverflow)?);216321642165        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2166        if list_exists {2167            let mut list = <AddressTokens<T>>::get(collection_id, owner);2168            let item_contains = list.contains(&item_index.clone());21692170            if item_contains {2171                list.retain(|&item| item != item_index);2172                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2173            }2174        }21752176        Ok(())2177    }21782179    fn move_token_index(2180        collection_id: CollectionId,2181        item_index: TokenId,2182        old_owner: &T::AccountId,2183        new_owner: &T::AccountId,2184    ) -> DispatchResult {2185        Self::remove_token_index(collection_id, item_index, old_owner)?;2186        Self::add_token_index(collection_id, item_index, new_owner)?;21872188        Ok(())2189    }2190    2191    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2192        ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);21932194        Ok(())2195    }2196}
addedpallets/scheduler/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/pallets/scheduler/Cargo.toml
@@ -0,0 +1,54 @@
+[package]
+name = "pallet-scheduler"
+version = "3.0.0"
+authors = ["Parity Technologies <admin@parity.io>"]
+edition = "2018"
+license = "Unlicense"
+homepage = "https://substrate.dev"
+repository = "https://github.com/paritytech/substrate/"
+description = "FRAME example pallet"
+readme = "README.md"
+
+[dependencies]
+serde = { version = "1.0.119" }
+codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false }
+frame-support = { default-features = false, version = '3.0.0' }
+frame-system = { default-features = false, version = '3.0.0' }
+pallet-nft-transaction-payment = { default-features = false, path = "../nft-transaction-payment" }
+pallet-nft = { default-features = false, path = "../nft" }
+nft-data-structs = { path = '../../primitives', default-features = false }
+
+pallet-contracts = { default-features = false, version = '3.0.0' }
+sp-runtime = { default-features = false, version = '3.0.0' }
+sp-std = { default-features = false, version = '3.0.0' }
+sp-io = { default-features = false, version = '3.0.0' }
+log = { version = "0.4.14", default-features = false }
+
+frame-benchmarking = { default-features = false, version = '3.0.0', optional = true }
+
+[dev-dependencies]
+sp-core = { default-features = false, version = '3.0.0' }
+substrate-test-utils = { version = "3.0.0" }
+
+[features]
+default = ["std"]
+std = [
+	"codec/std",
+	"sp-runtime/std",
+	"frame-benchmarking/std",
+	"frame-support/std",
+	"frame-system/std",
+	"pallet-nft-transaction-payment/std",
+	"pallet-nft/std",
+	"pallet-contracts/std",
+	"nft-data-structs/std",
+	"sp-io/std",
+	"sp-std/std",
+	"log/std",
+]
+runtime-benchmarks = [
+	"frame-benchmarking",
+	"frame-support/runtime-benchmarks",
+	"frame-system/runtime-benchmarks",
+]
+#try-runtime = ["frame-support/try-runtime"]
addedpallets/scheduler/README.mddiffbeforeafterboth
--- /dev/null
+++ b/pallets/scheduler/README.md
@@ -0,0 +1,34 @@
+# Scheduler
+A module for scheduling dispatches.
+
+- [`scheduler::Trait`](https://docs.rs/pallet-scheduler/latest/pallet_scheduler/trait.Trait.html)
+- [`Call`](https://docs.rs/pallet-scheduler/latest/pallet_scheduler/enum.Call.html)
+- [`Module`](https://docs.rs/pallet-scheduler/latest/pallet_scheduler/struct.Module.html)
+
+## Overview
+
+This module exposes capabilities for scheduling dispatches to occur at a
+specified block number or at a specified period. These scheduled dispatches
+may be named or anonymous and may be canceled.
+
+**NOTE:** The scheduled calls will be dispatched with the default filter
+for the origin: namely `frame_system::Config::BaseCallFilter` for all origin
+except root which will get no filter. And not the filter contained in origin
+use to call `fn schedule`.
+
+If a call is scheduled using proxy or whatever mecanism which adds filter,
+then those filter will not be used when dispatching the schedule call.
+
+## Interface
+
+### Dispatchable Functions
+
+* `schedule` - schedule a dispatch, which may be periodic, to occur at a
+  specified block and with a specified priority.
+* `cancel` - cancel a scheduled dispatch, specified by block number and
+  index.
+* `schedule_named` - augments the `schedule` interface with an additional
+  `Vec<u8>` parameter that can be used for identification.
+* `cancel_named` - the named complement to the cancel function.
+
+License: Unlicense
addedpallets/scheduler/src/benchmarking.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/scheduler/src/benchmarking.rs
@@ -0,0 +1,148 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2020-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.
+
+//! Scheduler pallet benchmarking.
+
+#![cfg(feature = "runtime-benchmarks")]
+
+use super::*;
+use sp_std::{vec, prelude::*};
+use frame_system::RawOrigin;
+use frame_support::{ensure, traits::OnInitialize};
+use frame_benchmarking::{benchmarks, impl_benchmark_test_suite};
+
+use crate::Module as Scheduler;
+use frame_system::Pallet as System;
+
+const BLOCK_NUMBER: u32 = 2;
+
+// Add `n` named items to the schedule
+fn fill_schedule<T: Config> (when: T::BlockNumber, n: u32) -> Result<(), &'static str> {
+	// Essentially a no-op call.
+	let call = frame_system::Call::set_storage(vec![]);
+	for i in 0..n {
+		// Named schedule is strictly heavier than anonymous
+		Scheduler::<T>::do_schedule_named(
+			i.encode(),
+			DispatchTime::At(when),
+			// Add periodicity
+			Some((T::BlockNumber::one(), 100)),
+			// HARD_DEADLINE priority means it gets executed no matter what
+			0,
+			frame_system::RawOrigin::Root.into(),
+			call.clone().into(),
+		)?;
+	}
+	ensure!(Agenda::<T>::get(when).len() == n as usize, "didn't fill schedule");
+	Ok(())
+}
+
+benchmarks! {
+	schedule {
+		let s in 0 .. T::MaxScheduledPerBlock::get();
+		let when = BLOCK_NUMBER.into();
+		let periodic = Some((T::BlockNumber::one(), 100));
+		let priority = 0;
+		// Essentially a no-op call.
+		let call = Box::new(frame_system::Call::set_storage(vec![]).into());
+
+		fill_schedule::<T>(when, s)?;
+	}: _(RawOrigin::Root, when, periodic, priority, call)
+	verify {
+		ensure!(
+			Agenda::<T>::get(when).len() == (s + 1) as usize,
+			"didn't add to schedule"
+		);
+	}
+
+	cancel {
+		let s in 1 .. T::MaxScheduledPerBlock::get();
+		let when = BLOCK_NUMBER.into();
+
+		fill_schedule::<T>(when, s)?;
+		assert_eq!(Agenda::<T>::get(when).len(), s as usize);
+	}: _(RawOrigin::Root, when, 0)
+	verify {
+		ensure!(
+			Lookup::<T>::get(0.encode()).is_none(),
+			"didn't remove from lookup"
+		);
+		// Removed schedule is NONE
+		ensure!(
+			Agenda::<T>::get(when)[0].is_none(),
+			"didn't remove from schedule"
+		);
+	}
+
+	schedule_named {
+		let s in 0 .. T::MaxScheduledPerBlock::get();
+		let id = s.encode();
+		let when = BLOCK_NUMBER.into();
+		let periodic = Some((T::BlockNumber::one(), 100));
+		let priority = 0;
+		// Essentially a no-op call.
+		let call = Box::new(frame_system::Call::set_storage(vec![]).into());
+
+		fill_schedule::<T>(when, s)?;
+	}: _(RawOrigin::Root, id, when, periodic, priority, call)
+	verify {
+		ensure!(
+			Agenda::<T>::get(when).len() == (s + 1) as usize,
+			"didn't add to schedule"
+		);
+	}
+
+	cancel_named {
+		let s in 1 .. T::MaxScheduledPerBlock::get();
+		let when = BLOCK_NUMBER.into();
+
+		fill_schedule::<T>(when, s)?;
+	}: _(RawOrigin::Root, 0.encode())
+	verify {
+		ensure!(
+			Lookup::<T>::get(0.encode()).is_none(),
+			"didn't remove from lookup"
+		);
+		// Removed schedule is NONE
+		ensure!(
+			Agenda::<T>::get(when)[0].is_none(),
+			"didn't remove from schedule"
+		);
+	}
+
+	// TODO [#7141]: Make this more complex and flexible so it can be used in automation.
+	#[extra]
+	on_initialize {
+		let s in 0 .. T::MaxScheduledPerBlock::get();
+		let when = BLOCK_NUMBER.into();
+		fill_schedule::<T>(when, s)?;
+	}: { Scheduler::<T>::on_initialize(BLOCK_NUMBER.into()); }
+	verify {
+		assert_eq!(System::<T>::event_count(), s);
+		// Next block should have all the schedules again
+		ensure!(
+			Agenda::<T>::get(when + T::BlockNumber::one()).len() == s as usize,
+			"didn't append schedule"
+		);
+	}
+}
+
+impl_benchmark_test_suite!(
+	Scheduler,
+	crate::tests::new_test_ext(),
+	crate::tests::Test,
+);
addedpallets/scheduler/src/lib.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/scheduler/src/lib.rs
@@ -0,0 +1,1611 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2017-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.
+
+//! # Scheduler
+//! A module for scheduling dispatches.
+//!
+//! - [`Config`]
+//! - [`Call`]
+//! - [`Module`]
+//!
+//! ## Overview
+//!
+//! This module exposes capabilities for scheduling dispatches to occur at a
+//! specified block number or at a specified period. These scheduled dispatches
+//! may be named or anonymous and may be canceled.
+//!
+//! **NOTE:** The scheduled calls will be dispatched with the default filter
+//! for the origin: namely `frame_system::Config::BaseCallFilter` for all origin
+//! except root which will get no filter. And not the filter contained in origin
+//! use to call `fn schedule`.
+//!
+//! If a call is scheduled using proxy or whatever mecanism which adds filter,
+//! then those filter will not be used when dispatching the schedule call.
+//!
+//! ## Interface
+//!
+//! ### Dispatchable Functions
+//!
+//! * `schedule` - schedule a dispatch, which may be periodic, to occur at a
+//!   specified block and with a specified priority.
+//! * `cancel` - cancel a scheduled dispatch, specified by block number and
+//!   index.
+//! * `schedule_named` - augments the `schedule` interface with an additional
+//!   `Vec<u8>` parameter that can be used for identification.
+//! * `cancel_named` - the named complement to the cancel function.
+
+// Ensure we're `no_std` when compiling for Wasm.
+#![cfg_attr(not(feature = "std"), no_std)]
+
+mod benchmarking;
+pub mod weights;
+
+use sp_std::{prelude::*, marker::PhantomData, borrow::Borrow};
+use codec::{Encode, Decode, Codec};
+use sp_runtime::{RuntimeDebug, traits::{Zero, One, BadOrigin, Saturating}};
+use frame_support::{
+	decl_module, decl_storage, decl_event, decl_error, IterableStorageMap,
+	dispatch::{Dispatchable, DispatchError, DispatchResult, Parameter},
+	traits::{Get, schedule::{self, DispatchTime}, OriginTrait, EnsureOrigin, IsType},
+	weights::{GetDispatchInfo, Weight},
+};
+use frame_system::{self as system, ensure_signed};
+use pallet_nft::*;
+// use pallet_nft_transaction_payment::{self as nft_transaction_payment};
+use nft_data_structs::*;
+pub use weights::WeightInfo;
+
+/// Our pallet's configuration trait. All our types and constants go in here. If the
+/// pallet is dependent on specific other pallets, then their configuration traits
+/// should be added to our implied traits list.
+///
+/// `system::Config` should always be included in our implied traits.
+/// // 
+pub trait Config: system::Config
+{ 
+
+	/// The overarching event type.
+	type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;
+
+	/// The aggregated origin which the dispatch will take.
+	type Origin: OriginTrait<PalletsOrigin = Self::PalletsOrigin>
+		+ From<Self::PalletsOrigin> + IsType<<Self as system::Config>::Origin>;
+
+	/// The caller origin, overarching type of all pallets origins.
+	type PalletsOrigin: From<system::RawOrigin<Self::AccountId>> + Codec + Clone + Eq;
+
+	/// The aggregated call type.
+	type Call: Parameter + Dispatchable<Origin=<Self as Config>::Origin> + GetDispatchInfo + From<system::Call<Self>>;
+
+	/// The maximum weight that may be scheduled per block for any dispatchables of less priority
+	/// than `schedule::HARD_DEADLINE`.
+	type MaximumWeight: Get<Weight>;
+
+	/// Required origin to schedule or cancel calls.
+	type ScheduleOrigin: EnsureOrigin<<Self as system::Config>::Origin>;
+
+	/// The maximum number of scheduled calls in the queue for a single block.
+	/// Not strictly enforced, but used for weight estimation.
+	type MaxScheduledPerBlock: Get<u32>;
+
+	/// TODO
+	type Sponsoring: SponsoringResolve<Self::AccountId, <Self as Config>::Call>;
+
+	/// Weight information for extrinsics in this pallet.
+	type WeightInfo: WeightInfo;
+}
+
+// pub type SelfWeightInfo<T> = <T as system::Config>::WeightInfo;
+
+/// Just a simple index for naming period tasks.
+pub type PeriodicIndex = u32;
+/// The location of a scheduled task that can be used to remove it.
+pub type TaskAddress<BlockNumber> = (BlockNumber, u32);
+
+#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))]
+#[derive(Clone, RuntimeDebug, Encode, Decode)]
+struct ScheduledV1<Call, BlockNumber> {
+	maybe_id: Option<Vec<u8>>,
+	priority: schedule::Priority,
+	call: Call,
+	maybe_periodic: Option<schedule::Period<BlockNumber>>,
+}
+
+/// Information regarding an item to be executed in the future.
+#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))]
+#[derive(Clone, RuntimeDebug, Encode, Decode)]
+pub struct ScheduledV2<Call, BlockNumber, PalletsOrigin, AccountId> {
+	/// The unique identity for this task, if there is one.
+	maybe_id: Option<Vec<u8>>,
+	/// This task's priority.
+	priority: schedule::Priority,
+	/// The call to be dispatched.
+	call: Call,
+	/// If the call is periodic, then this points to the information concerning that.
+	maybe_periodic: Option<schedule::Period<BlockNumber>>,
+	/// The origin to dispatch the call.
+	origin: PalletsOrigin,
+	_phantom: PhantomData<AccountId>,
+}
+
+/// The current version of Scheduled struct.
+pub type Scheduled<Call, BlockNumber, PalletsOrigin, AccountId> = ScheduledV2<Call, BlockNumber, PalletsOrigin, AccountId>;
+
+// A value placed in storage that represents the current version of the Scheduler storage.
+// This value is used by the `on_runtime_upgrade` logic to determine whether we run
+// storage migration logic.
+#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug)]
+enum Releases {
+	V1,
+	V2,
+}
+
+impl Default for Releases {
+	fn default() -> Self {
+		Releases::V1
+	}
+}
+
+#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
+pub struct CallSpec {
+
+	module: u32,
+	method: u32,
+}
+
+decl_storage! {
+	trait Store for Module<T: Config> as Scheduler {
+		/// Items to be executed, indexed by the block number that they should be executed on.
+		pub Agenda: map hasher(twox_64_concat) T::BlockNumber
+			=> Vec<Option<Scheduled<<T as Config>::Call, T::BlockNumber, T::PalletsOrigin, T::AccountId>>>;
+
+		pub SpecAgenda: map hasher(twox_64_concat) T::BlockNumber
+			=> Vec<Option<CallSpec>>;			
+
+		/// Lookup from identity to the block number and index of the task.
+		Lookup: map hasher(twox_64_concat) Vec<u8> => Option<TaskAddress<T::BlockNumber>>;
+
+		/// Storage version of the pallet.
+		///
+		/// New networks start with last version.
+		StorageVersion build(|_| Releases::V2): Releases;
+	}
+}
+
+decl_event!(
+	pub enum Event<T> where <T as system::Config>::BlockNumber {
+		/// Scheduled some task. \[when, index\]
+		Scheduled(BlockNumber, u32),
+		/// Canceled some task. \[when, index\]
+		Canceled(BlockNumber, u32),
+		/// Dispatched some task. \[task, id, result\]
+		Dispatched(TaskAddress<BlockNumber>, Option<Vec<u8>>, DispatchResult),
+	}
+);
+
+decl_error! {
+	pub enum Error for Module<T: Config> {
+		/// Failed to schedule a call
+		FailedToSchedule,
+		/// Cannot find the scheduled call.
+		NotFound,
+		/// Given target block number is in the past.
+		TargetBlockNumberInPast,
+		/// Reschedule failed because it does not change scheduled time.
+		RescheduleNoChange,
+	}
+}
+
+decl_module! {
+	/// Scheduler module declaration.
+	pub struct Module<T: Config> for enum Call 
+	where 
+		origin: <T as system::Config>::Origin
+	{
+		type Error = Error<T>;
+		fn deposit_event() = default;
+
+
+		/// Anonymously schedule a task.
+		///
+		/// # <weight>
+		/// - S = Number of already scheduled calls
+		/// - Base Weight: 22.29 + .126 * S µs
+		/// - DB Weight:
+		///     - Read: Agenda
+		///     - Write: Agenda
+		/// - Will use base weight of 25 which should be good for up to 30 scheduled calls
+		/// # </weight>
+		// #[weight = <T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get())]
+		#[weight = 0]
+		fn schedule(origin,
+			when: T::BlockNumber,
+			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,
+			priority: schedule::Priority,
+			call: Box<<T as Config>::Call>,
+		) 
+		{
+			let origin = <T as Config>::Origin::from(origin);
+			Self::do_schedule(DispatchTime::At(when), maybe_periodic, priority, origin.caller().clone(), *call)?;
+		}
+
+		/// Cancel an anonymously scheduled task.
+		///
+		/// # <weight>
+		/// - S = Number of already scheduled calls
+		/// - Base Weight: 22.15 + 2.869 * S µs
+		/// - DB Weight:
+		///     - Read: Agenda
+		///     - Write: Agenda, Lookup
+		/// - Will use base weight of 100 which should be good for up to 30 scheduled calls
+		/// # </weight>
+		#[weight = <T as Config>::WeightInfo::cancel(T::MaxScheduledPerBlock::get())]
+		fn cancel(origin, when: T::BlockNumber, index: u32) {
+			T::ScheduleOrigin::ensure_origin(origin.clone())?;
+			let origin = <T as Config>::Origin::from(origin);
+			Self::do_cancel(Some(origin.caller().clone()), (when, index))?;
+		}
+
+		/// Schedule a named task.
+		///
+		/// # <weight>
+		/// - S = Number of already scheduled calls
+		/// - Base Weight: 29.6 + .159 * S µs
+		/// - DB Weight:
+		///     - Read: Agenda, Lookup
+		///     - Write: Agenda, Lookup
+		/// - Will use base weight of 35 which should be good for more than 30 scheduled calls
+		/// # </weight>
+		#[weight = <T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get())]
+		fn schedule_named(origin,
+			id: Vec<u8>,
+			when: T::BlockNumber,
+			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,
+			priority: schedule::Priority,
+			call: Box<<T as Config>::Call>,
+		) {
+			T::ScheduleOrigin::ensure_origin(origin.clone())?;
+			let origin = <T as Config>::Origin::from(origin);
+			Self::do_schedule_named(
+				id, DispatchTime::At(when), maybe_periodic, priority, origin.caller().clone(), *call
+			)?;
+		}
+
+		/// Cancel a named scheduled task.
+		///
+		/// # <weight>
+		/// - S = Number of already scheduled calls
+		/// - Base Weight: 24.91 + 2.907 * S µs
+		/// - DB Weight:
+		///     - Read: Agenda, Lookup
+		///     - Write: Agenda, Lookup
+		/// - Will use base weight of 100 which should be good for up to 30 scheduled calls
+		/// # </weight>
+		#[weight = <T as Config>::WeightInfo::cancel_named(T::MaxScheduledPerBlock::get())]
+		fn cancel_named(origin, id: Vec<u8>) {
+			T::ScheduleOrigin::ensure_origin(origin.clone())?;
+			let origin = <T as Config>::Origin::from(origin);
+			Self::do_cancel_named(Some(origin.caller().clone()), id)?;
+		}
+
+		/// Anonymously schedule a task after a delay.
+		///
+		/// # <weight>
+		/// Same as [`schedule`].
+		/// # </weight>
+		#[weight = <T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get())]
+		fn schedule_after(origin,
+			after: T::BlockNumber,
+			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,
+			priority: schedule::Priority,
+			call: Box<<T as Config>::Call>,
+		) {
+			T::ScheduleOrigin::ensure_origin(origin.clone())?;
+			let origin = <T as Config>::Origin::from(origin);
+			Self::do_schedule(
+				DispatchTime::After(after), maybe_periodic, priority, origin.caller().clone(), *call
+			)?;
+		}
+
+		/// Schedule a named task after a delay.
+		///
+		/// # <weight>
+		/// Same as [`schedule_named`].
+		/// # </weight>
+		#[weight = <T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get())]
+		fn schedule_named_after(origin,
+			id: Vec<u8>,
+			after: T::BlockNumber,
+			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,
+			priority: schedule::Priority,
+			call: Box<<T as Config>::Call>,
+		) {
+			T::ScheduleOrigin::ensure_origin(origin.clone())?;
+			let origin = <T as Config>::Origin::from(origin);
+			Self::do_schedule_named(
+				id, DispatchTime::After(after), maybe_periodic, priority, origin.caller().clone(), *call
+			)?;
+		}
+
+		/// Execute the scheduled calls
+		///
+		/// # <weight>
+		/// - S = Number of already scheduled calls
+		/// - N = Named scheduled calls
+		/// - P = Periodic Calls
+		/// - Base Weight: 9.243 + 23.45 * S µs
+		/// - DB Weight:
+		///     - Read: Agenda + Lookup * N + Agenda(Future) * P
+		///     - Write: Agenda + Lookup * N  + Agenda(future) * P
+		/// # </weight>
+		fn on_initialize(now: T::BlockNumber) -> Weight {
+			let limit = T::MaximumWeight::get();
+			let mut queued = Agenda::<T>::take(now).into_iter()
+				.enumerate()
+				.filter_map(|(index, s)| s.map(|inner| (index as u32, inner)))
+				.collect::<Vec<_>>();
+			if queued.len() as u32 > T::MaxScheduledPerBlock::get() {
+				log::warn!(
+					target: "runtime::scheduler",
+					"Warning: This block has more items queued in Scheduler than \
+					expected from the runtime configuration. An update might be needed."
+				);
+			}
+			queued.sort_by_key(|(_, s)| s.priority);
+			let base_weight: Weight = T::DbWeight::get().reads_writes(1, 2); // Agenda + Agenda(next)
+			let mut total_weight: Weight = <T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get());
+			queued.into_iter()
+				.enumerate()
+				.scan(base_weight, |cumulative_weight, (order, (index, s))| {
+					*cumulative_weight = cumulative_weight
+						.saturating_add(s.call.get_dispatch_info().weight);
+
+					let origin = <<T as Config>::Origin as From<T::PalletsOrigin>>::from(
+						s.origin.clone()
+					).into();
+
+					if ensure_signed(origin).is_ok() {
+						 // AccountData for inner call origin accountdata.
+						*cumulative_weight = cumulative_weight
+							.saturating_add(T::DbWeight::get().reads_writes(1, 1));
+					}
+
+					if s.maybe_id.is_some() {
+						// Remove/Modify Lookup
+						*cumulative_weight = cumulative_weight.saturating_add(T::DbWeight::get().writes(1));
+					}
+					if s.maybe_periodic.is_some() {
+						// Read/Write Agenda for future block
+						*cumulative_weight = cumulative_weight.saturating_add(T::DbWeight::get().reads_writes(1, 1));
+					}
+
+					Some((order, index, *cumulative_weight, s))
+				})
+				.filter_map(|(order, index, cumulative_weight, mut s)| {
+					// We allow a scheduled call if any is true:
+					// - It's priority is `HARD_DEADLINE`
+					// - It does not push the weight past the limit.
+					// - It is the first item in the schedule
+					if s.priority <= schedule::HARD_DEADLINE || cumulative_weight <= limit || order == 0 {
+
+						let origin = <<T as Config>::Origin as From<T::PalletsOrigin>>::from(
+							s.origin.clone()
+						).into();
+						let sender = ensure_signed(origin).unwrap_or(T::AccountId::default());
+						let who_will_pay = T::Sponsoring::resolve(&sender, &s.call.clone()).unwrap_or(
+							T::AccountId::default());
+						let sponsor = T::PalletsOrigin::from(system::RawOrigin::Signed(who_will_pay));
+						let r = s.call.clone().dispatch(sponsor.into());
+						let maybe_id = s.maybe_id.clone();
+						if let &Some((period, count)) = &s.maybe_periodic {
+							if count > 1 {
+								s.maybe_periodic = Some((period, count - 1));
+							} else {
+								s.maybe_periodic = None;
+							}
+							let next = now + period;
+							// If scheduled is named, place it's information in `Lookup`
+							if let Some(ref id) = s.maybe_id {
+								let next_index = Agenda::<T>::decode_len(now + period).unwrap_or(0);
+								Lookup::<T>::insert(id, (next, next_index as u32));
+							}
+							Agenda::<T>::append(next, Some(s));
+						} else {
+							if let Some(ref id) = s.maybe_id {
+								Lookup::<T>::remove(id);
+							}
+						}
+						Self::deposit_event(RawEvent::Dispatched(
+							(now, index),
+							maybe_id,
+							r.map(|_| ()).map_err(|e| e.error)
+						));
+						total_weight = cumulative_weight;
+						None
+					} else {
+						Some(Some(s))
+					}
+				})
+				.for_each(|unused| {
+					let next = now + One::one();
+					Agenda::<T>::append(next, unused);
+				});
+
+			total_weight
+		}
+	}
+}
+
+impl<T: Config> Module<T> {
+	/// Migrate storage format from V1 to V2.
+	/// Return true if migration is performed.
+	pub fn migrate_v1_to_t2() -> bool {
+		if StorageVersion::get() == Releases::V1 {
+			StorageVersion::put(Releases::V2);
+
+			Agenda::<T>::translate::<
+				Vec<Option<ScheduledV1<<T as Config>::Call, T::BlockNumber>>>, _
+			>(|_, agenda| Some(
+				agenda
+					.into_iter()
+					.map(|schedule| schedule.map(|schedule| ScheduledV2 {
+						maybe_id: schedule.maybe_id,
+						priority: schedule.priority,
+						call: schedule.call,
+						maybe_periodic: schedule.maybe_periodic,
+						origin: system::RawOrigin::Root.into(),
+						_phantom: Default::default(),
+					}))
+					.collect::<Vec<_>>()
+			));
+
+			true
+		} else {
+			false
+		}
+	}
+
+	/// Helper to migrate scheduler when the pallet origin type has changed.
+	pub fn migrate_origin<OldOrigin: Into<T::PalletsOrigin> + codec::Decode>() {
+		Agenda::<T>::translate::<
+			Vec<Option<Scheduled<<T as Config>::Call, T::BlockNumber, OldOrigin, T::AccountId>>>, _
+		>(|_, agenda| Some(
+			agenda
+				.into_iter()
+				.map(|schedule| schedule.map(|schedule| Scheduled {
+					maybe_id: schedule.maybe_id,
+					priority: schedule.priority,
+					call: schedule.call,
+					maybe_periodic: schedule.maybe_periodic,
+					origin: schedule.origin.into(),
+					_phantom: Default::default(),
+				}))
+				.collect::<Vec<_>>()
+		));
+	}
+
+	fn resolve_time(when: DispatchTime<T::BlockNumber>) -> Result<T::BlockNumber, DispatchError> {
+		let now = frame_system::Pallet::<T>::block_number();
+
+		let when = match when {
+			DispatchTime::At(x) => x,
+			// The current block has already completed it's scheduled tasks, so
+			// Schedule the task at lest one block after this current block.
+			DispatchTime::After(x) => now.saturating_add(x).saturating_add(One::one())
+		};
+
+		if when <= now {
+			return Err(Error::<T>::TargetBlockNumberInPast.into())
+		}
+
+		Ok(when)
+	}
+
+	fn do_schedule(
+		when: DispatchTime<T::BlockNumber>,
+		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,
+		priority: schedule::Priority,
+		origin: T::PalletsOrigin,
+		call: <T as Config>::Call
+	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {
+		let when = Self::resolve_time(when)?;
+
+		// sanitize maybe_periodic
+		let maybe_periodic = maybe_periodic
+			.filter(|p| p.1 > 1 && !p.0.is_zero())
+			// Remove one from the number of repetitions since we will schedule one now.
+			.map(|(p, c)| (p, c - 1));
+		let s = Some(Scheduled {
+			maybe_id: None, priority, call, maybe_periodic, origin, _phantom: PhantomData::<T::AccountId>::default(),
+		});
+		Agenda::<T>::append(when, s);
+		let index = Agenda::<T>::decode_len(when).unwrap_or(1) as u32 - 1;
+		if index > T::MaxScheduledPerBlock::get() {
+			log::warn!(
+				target: "runtime::scheduler",
+				"Warning: There are more items queued in the Scheduler than \
+				expected from the runtime configuration. An update might be needed.",
+			);
+		}
+		Self::deposit_event(RawEvent::Scheduled(when, index));
+
+		Ok((when, index))
+	}
+
+	fn do_cancel(
+		origin: Option<T::PalletsOrigin>,
+		(when, index): TaskAddress<T::BlockNumber>
+	) -> Result<(), DispatchError> {
+		let scheduled = Agenda::<T>::try_mutate(
+			when,
+			|agenda| {
+				agenda.get_mut(index as usize)
+					.map_or(Ok(None), |s| -> Result<Option<Scheduled<_, _, _, _>>, DispatchError> {
+						if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {
+							if *o != s.origin {
+								return Err(BadOrigin.into());
+							}
+						};
+						Ok(s.take())
+					})
+			},
+		)?;
+		if let Some(s) = scheduled {
+			if let Some(id) = s.maybe_id {
+				Lookup::<T>::remove(id);
+			}
+			Self::deposit_event(RawEvent::Canceled(when, index));
+			Ok(())
+		} else {
+			Err(Error::<T>::NotFound)?
+		}
+	}
+
+	fn do_reschedule(
+		(when, index): TaskAddress<T::BlockNumber>,
+		new_time: DispatchTime<T::BlockNumber>,
+	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {
+		let new_time = Self::resolve_time(new_time)?;
+
+		if new_time == when {
+			return Err(Error::<T>::RescheduleNoChange.into());
+		}
+
+		Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {
+			let task = agenda.get_mut(index as usize).ok_or(Error::<T>::NotFound)?;
+			let task = task.take().ok_or(Error::<T>::NotFound)?;
+			Agenda::<T>::append(new_time, Some(task));
+			Ok(())
+		})?;
+
+		let new_index = Agenda::<T>::decode_len(new_time).unwrap_or(1) as u32 - 1;
+		Self::deposit_event(RawEvent::Canceled(when, index));
+		Self::deposit_event(RawEvent::Scheduled(new_time, new_index));
+
+		Ok((new_time, new_index))
+	}
+
+	fn do_schedule_named(
+		id: Vec<u8>,
+		when: DispatchTime<T::BlockNumber>,
+		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,
+		priority: schedule::Priority,
+		origin: T::PalletsOrigin,
+		call: <T as Config>::Call,
+	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {
+		// ensure id it is unique
+		if Lookup::<T>::contains_key(&id) {
+			return Err(Error::<T>::FailedToSchedule)?
+		}
+
+		let when = Self::resolve_time(when)?;
+
+		// sanitize maybe_periodic
+		let maybe_periodic = maybe_periodic
+			.filter(|p| p.1 > 1 && !p.0.is_zero())
+			// Remove one from the number of repetitions since we will schedule one now.
+			.map(|(p, c)| (p, c - 1));
+
+		let s = Scheduled {
+			maybe_id: Some(id.clone()), priority, call, maybe_periodic, origin, _phantom: Default::default()
+		};
+		Agenda::<T>::append(when, Some(s));
+		let index = Agenda::<T>::decode_len(when).unwrap_or(1) as u32 - 1;
+		if index > T::MaxScheduledPerBlock::get() {
+			log::warn!(
+				target: "runtime::scheduler",
+				"Warning: There are more items queued in the Scheduler than \
+				expected from the runtime configuration. An update might be needed.",
+			);
+		}
+		let address = (when, index);
+		Lookup::<T>::insert(&id, &address);
+		Self::deposit_event(RawEvent::Scheduled(when, index));
+
+		Ok(address)
+	}
+
+	fn do_cancel_named(origin: Option<T::PalletsOrigin>, id: Vec<u8>) -> DispatchResult {
+		Lookup::<T>::try_mutate_exists(id, |lookup| -> DispatchResult {
+			if let Some((when, index)) = lookup.take() {
+				let i = index as usize;
+				Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {
+					if let Some(s) = agenda.get_mut(i) {
+						if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {
+							if *o != s.origin {
+								return Err(BadOrigin.into());
+							}
+						}
+						*s = None;
+					}
+					Ok(())
+				})?;
+				Self::deposit_event(RawEvent::Canceled(when, index));
+				Ok(())
+			} else {
+				Err(Error::<T>::NotFound)?
+			}
+		})
+	}
+
+	fn do_reschedule_named(
+		id: Vec<u8>,
+		new_time: DispatchTime<T::BlockNumber>,
+	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {
+		let new_time = Self::resolve_time(new_time)?;
+
+		Lookup::<T>::try_mutate_exists(id, |lookup| -> Result<TaskAddress<T::BlockNumber>, DispatchError> {
+			let (when, index) = lookup.ok_or(Error::<T>::NotFound)?;
+
+			if new_time == when {
+				return Err(Error::<T>::RescheduleNoChange.into());
+			}
+
+			Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {
+				let task = agenda.get_mut(index as usize).ok_or(Error::<T>::NotFound)?;
+				let task = task.take().ok_or(Error::<T>::NotFound)?;
+				Agenda::<T>::append(new_time, Some(task));
+
+				Ok(())
+			})?;
+
+			let new_index = Agenda::<T>::decode_len(new_time).unwrap_or(1) as u32 - 1;
+			Self::deposit_event(RawEvent::Canceled(when, index));
+			Self::deposit_event(RawEvent::Scheduled(new_time, new_index));
+
+			*lookup = Some((new_time, new_index));
+
+			Ok((new_time, new_index))
+		})
+	}
+}
+
+impl<T: Config> schedule::Anon<T::BlockNumber, <T as Config>::Call, T::PalletsOrigin> for Module<T> {
+	type Address = TaskAddress<T::BlockNumber>;
+
+	fn schedule(
+		when: DispatchTime<T::BlockNumber>,
+		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,
+		priority: schedule::Priority,
+		origin: T::PalletsOrigin,
+		call: <T as Config>::Call
+	) -> Result<Self::Address, DispatchError> {
+		Self::do_schedule(when, maybe_periodic, priority, origin, call)
+	}
+
+	fn cancel((when, index): Self::Address) -> Result<(), ()> {
+		Self::do_cancel(None, (when, index)).map_err(|_| ())
+	}
+
+	fn reschedule(
+		address: Self::Address,
+		when: DispatchTime<T::BlockNumber>,
+	) -> Result<Self::Address, DispatchError> {
+		Self::do_reschedule(address, when)
+	}
+
+	fn next_dispatch_time((when, index): Self::Address) -> Result<T::BlockNumber, ()> {
+		Agenda::<T>::get(when).get(index as usize).ok_or(()).map(|_| when)
+	}
+}
+
+impl<T: Config> schedule::Named<T::BlockNumber, <T as Config>::Call, T::PalletsOrigin> for Module<T> {
+	type Address = TaskAddress<T::BlockNumber>;
+
+	fn schedule_named(
+		id: Vec<u8>,
+		when: DispatchTime<T::BlockNumber>,
+		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,
+		priority: schedule::Priority,
+		origin: T::PalletsOrigin,
+		call: <T as Config>::Call,
+	) -> Result<Self::Address, ()> {
+		Self::do_schedule_named(id, when, maybe_periodic, priority, origin, call).map_err(|_| ())
+	}
+
+	fn cancel_named(id: Vec<u8>) -> Result<(), ()> {
+		Self::do_cancel_named(None, id).map_err(|_| ())
+	}
+
+	fn reschedule_named(
+		id: Vec<u8>,
+		when: DispatchTime<T::BlockNumber>,
+	) -> Result<Self::Address, DispatchError> {
+		Self::do_reschedule_named(id, when)
+	}
+
+	fn next_dispatch_time(id: Vec<u8>) -> Result<T::BlockNumber, ()> {
+		Lookup::<T>::get(id).and_then(|(when, index)| Agenda::<T>::get(when).get(index as usize).map(|_| when)).ok_or(())
+	}
+}
+
+#[cfg(test)]
+mod tests {
+	use super::*;
+
+	use frame_support::{
+		parameter_types, assert_ok, ord_parameter_types,
+		assert_noop, assert_err, Hashable,
+		traits::{OnInitialize, OnFinalize, Filter},
+		weights::constants::RocksDbWeight,
+	};
+	use sp_core::H256;
+	use sp_runtime::{
+		Perbill,
+		testing::Header,
+		traits::{BlakeTwo256, IdentityLookup},
+	};
+	use frame_system::{EnsureOneOf, EnsureRoot, EnsureSignedBy};
+	use substrate_test_utils::assert_eq_uvec;
+	use crate as scheduler;
+
+	mod logger {
+		use super::*;
+		use std::cell::RefCell;
+
+		thread_local! {
+			static LOG: RefCell<Vec<(OriginCaller, u32)>> = RefCell::new(Vec::new());
+		}
+		pub fn log() -> Vec<(OriginCaller, u32)> {
+			LOG.with(|log| log.borrow().clone())
+		}
+		pub trait Config: system::Config {
+			type Event: From<Event> + Into<<Self as system::Config>::Event>;
+		}
+		decl_event! {
+			pub enum Event {
+				Logged(u32, Weight),
+			}
+		}
+		decl_module! {
+			pub struct Module<T: Config> for enum Call
+			where
+				origin: <T as system::Config>::Origin,
+				<T as system::Config>::Origin: OriginTrait<PalletsOrigin = OriginCaller>
+			{
+				fn deposit_event() = default;
+
+				#[weight = *weight]
+				fn log(origin, i: u32, weight: Weight) {
+					Self::deposit_event(Event::Logged(i, weight));
+					LOG.with(|log| {
+						log.borrow_mut().push((origin.caller().clone(), i));
+					})
+				}
+
+				#[weight = *weight]
+				fn log_without_filter(origin, i: u32, weight: Weight) {
+					Self::deposit_event(Event::Logged(i, weight));
+					LOG.with(|log| {
+						log.borrow_mut().push((origin.caller().clone(), i));
+					})
+				}
+			}
+		}
+	}
+
+	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>},
+			Logger: logger::{Pallet, Call, Event},
+			Scheduler: scheduler::{Pallet, Call, Storage, Event<T>},
+		}
+	);
+
+	// Scheduler must dispatch with root and no filter, this tests base filter is indeed not used.
+	pub struct BaseFilter;
+	impl Filter<Call> for BaseFilter {
+		fn filter(call: &Call) -> bool {
+			!matches!(call, Call::Logger(logger::Call::log(_, _)))
+		}
+	}
+
+	parameter_types! {
+		pub const BlockHashCount: u64 = 250;
+		pub BlockWeights: frame_system::limits::BlockWeights =
+			frame_system::limits::BlockWeights::simple_max(2_000_000_000_000);
+	}
+	impl system::Config for Test {
+		type BaseCallFilter = BaseFilter;
+		type BlockWeights = ();
+		type BlockLength = ();
+		type DbWeight = RocksDbWeight;
+		type Origin = Origin;
+		type Call = Call;
+		type Index = u64;
+		type BlockNumber = u64;
+		type Hash = H256;
+		type Hashing = BlakeTwo256;
+		type AccountId = u64;
+		type Lookup = IdentityLookup<Self::AccountId>;
+		type Header = Header;
+		type Event = Event;
+		type BlockHashCount = BlockHashCount;
+		type Version = ();
+		type PalletInfo = PalletInfo;
+		type AccountData = ();
+		type OnNewAccount = ();
+		type OnKilledAccount = ();
+		type SystemWeightInfo = ();
+		type SS58Prefix = ();
+		type OnSetCode = ();
+	}
+	impl logger::Config for Test {
+		type Event = Event;
+	}
+	parameter_types! {
+		pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * BlockWeights::get().max_block;
+		pub const MaxScheduledPerBlock: u32 = 10;
+	}
+	ord_parameter_types! {
+		pub const One: u64 = 1;
+	}
+
+	impl Config for Test {
+		type Event = Event;
+		type Origin = Origin;
+		type PalletsOrigin = OriginCaller;
+		type Call = Call;
+		type MaximumWeight = MaximumSchedulerWeight;
+		type ScheduleOrigin = EnsureOneOf<u64, EnsureRoot<u64>, EnsureSignedBy<One, u64>>;
+		type MaxScheduledPerBlock = MaxScheduledPerBlock;
+		type WeightInfo = ();
+	}
+
+	pub fn new_test_ext() -> sp_io::TestExternalities {
+		let t = system::GenesisConfig::default().build_storage::<Test>().unwrap();
+		t.into()
+	}
+
+	fn run_to_block(n: u64) {
+		while System::block_number() < n {
+			Scheduler::on_finalize(System::block_number());
+			System::set_block_number(System::block_number() + 1);
+			Scheduler::on_initialize(System::block_number());
+		}
+	}
+
+	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 Into<OriginCaller> for u32 {
+				fn into(self) -> OriginCaller {
+					match self {
+						3u32 => system::RawOrigin::Root.into(),
+						2u32 => system::RawOrigin::None.into(),
+						_ => unreachable!("test make no use of it"),
+					}
+				}
+			}
+
+			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(),
+						}),
+					]
+				)
+			]);
+		});
+	}
+}
addedpallets/scheduler/src/weights.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/scheduler/src/weights.rs
@@ -0,0 +1,118 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2020-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.
+
+//! Weights for pallet_scheduler
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0
+//! DATE: 2020-10-27, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: [], HIGH RANGE: []
+//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128
+
+// Executed Command:
+// target/release/substrate
+// benchmark
+// --chain=dev
+// --steps=50
+// --repeat=20
+// --pallet=pallet_scheduler
+// --extrinsic=*
+// --execution=wasm
+// --wasm-execution=compiled
+// --heap-pages=4096
+// --output=./frame/scheduler/src/weights.rs
+// --template=./.maintain/frame-weight-template.hbs
+
+
+#![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_scheduler.
+pub trait WeightInfo {
+	fn schedule(s: u32, ) -> Weight;
+	fn cancel(s: u32, ) -> Weight;
+	fn schedule_named(s: u32, ) -> Weight;
+	fn cancel_named(s: u32, ) -> Weight;
+	
+}
+
+/// Weights for pallet_scheduler using the Substrate node and recommended hardware.
+pub struct SubstrateWeight<T>(PhantomData<T>);
+impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
+	fn schedule(s: u32, ) -> Weight {
+		(35_029_000 as Weight)
+			.saturating_add((77_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+			
+	}
+	fn cancel(s: u32, ) -> Weight {
+		(31_419_000 as Weight)
+			.saturating_add((4_015_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(2 as Weight))
+			
+	}
+	fn schedule_named(s: u32, ) -> Weight {
+		(44_752_000 as Weight)
+			.saturating_add((123_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(T::DbWeight::get().reads(2 as Weight))
+			.saturating_add(T::DbWeight::get().writes(2 as Weight))
+			
+	}
+	fn cancel_named(s: u32, ) -> Weight {
+		(35_712_000 as Weight)
+			.saturating_add((4_008_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(T::DbWeight::get().reads(2 as Weight))
+			.saturating_add(T::DbWeight::get().writes(2 as Weight))
+			
+	}
+	
+}
+
+// For backwards compatibility and tests
+impl WeightInfo for () {
+	fn schedule(s: u32, ) -> Weight {
+		(35_029_000 as Weight)
+			.saturating_add((77_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+			
+	}
+	fn cancel(s: u32, ) -> Weight {
+		(31_419_000 as Weight)
+			.saturating_add((4_015_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(2 as Weight))
+			
+	}
+	fn schedule_named(s: u32, ) -> Weight {
+		(44_752_000 as Weight)
+			.saturating_add((123_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(2 as Weight))
+			
+	}
+	fn cancel_named(s: u32, ) -> Weight {
+		(35_712_000 as Weight)
+			.saturating_add((4_008_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(2 as Weight))
+			
+	}
+	
+}
addedprimitives/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/primitives/Cargo.toml
@@ -0,0 +1,30 @@
+[package]
+name = "nft-data-structs"
+authors = ['Substrate DevHub <https://github.com/substrate-developer-hub>']
+description = "Nft data structs definitions"
+edition = "2018"
+license = 'GPL-3.0'
+homepage = "https://substrate.dev"
+repository = 'https://github.com/clover-network/clover'
+version = '0.9.0'
+
+[dependencies]
+codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false }
+serde = { version = "1.0.101", optional = true }
+frame-support = { default-features = false, version = '3.0.0' }
+frame-system = { default-features = false, version = '3.0.0' }
+pallet-contracts = { default-features = false, version = '3.0.0' }
+sp-core = { version = "3.0.0", default-features = false  }
+sp-runtime = { version = "3.0.0", default-features = false  }
+
+[features]
+default = ["std"]
+std = [
+  "serde",
+  "codec/std",
+  "frame-system/std",
+  "frame-support/std",
+  "sp-runtime/std",
+  "sp-core/std",
+  "pallet-contracts/std",
+]
\ No newline at end of file
addedprimitives/src/lib.rsdiffbeforeafterboth
--- /dev/null
+++ b/primitives/src/lib.rs
@@ -0,0 +1,306 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+
+use codec::{Decode, Encode};
+
+#[cfg(feature = "std")]
+use serde::{Deserialize, Serialize};
+
+use frame_system;
+use core::ops::{Deref, DerefMut};
+use sp_runtime::sp_std::prelude::Vec;
+pub use frame_support::{
+    construct_runtime, decl_event, decl_module, decl_storage, decl_error,
+    dispatch::DispatchResult,
+    ensure, fail, parameter_types,
+    traits::{
+        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,
+        Randomness, IsSubType, WithdrawReasons,
+    },
+    weights::{
+        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
+        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,
+        WeightToFeePolynomial, DispatchClass,
+    },
+    StorageValue,
+    transactional,
+};
+
+pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;
+pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;
+pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;
+pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;
+
+pub type CollectionId = u32;
+pub type TokenId = u32;
+pub type DecimalPoints = u8;
+
+#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub enum CollectionMode {
+    Invalid,
+    NFT,
+    // decimal points
+    Fungible(DecimalPoints),
+    ReFungible,
+}
+
+impl Default for CollectionMode {
+    fn default() -> Self {
+        Self::Invalid
+    }
+}
+
+impl Into<u8> for CollectionMode {
+    fn into(self) -> u8 {
+        match self {
+            CollectionMode::Invalid => 0,
+            CollectionMode::NFT => 1,
+            CollectionMode::Fungible(_) => 2,
+            CollectionMode::ReFungible => 3,
+        }
+    }
+}
+
+pub trait SponsoringResolve<AccountId, Call> {
+    fn resolve(
+        who: &AccountId,
+		call: &Call) -> Option<AccountId>;
+}
+
+#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub enum AccessMode {
+    Normal,
+    WhiteList,
+}
+impl Default for AccessMode {
+    fn default() -> Self {
+        Self::Normal
+    }
+}
+
+#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub enum SchemaVersion {
+    ImageURL,
+    Unique,
+}
+impl Default for SchemaVersion {
+    fn default() -> Self {
+        Self::ImageURL
+    }
+}
+
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct Ownership<AccountId> {
+    pub owner: AccountId,
+    pub fraction: u128,
+}
+
+#[derive(Encode, Decode, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub enum SponsorshipState<AccountId> {
+    /// The fees are applied to the transaction sender
+    Disabled,
+    Unconfirmed(AccountId),
+    /// Transactions are sponsored by specified account
+    Confirmed(AccountId),
+}
+
+impl<AccountId> SponsorshipState<AccountId> {
+    pub fn sponsor(&self) -> Option<&AccountId> {
+        match self {
+            Self::Confirmed(sponsor) => Some(sponsor),
+            _ => None,
+        }
+    }
+
+    pub fn pending_sponsor(&self) -> Option<&AccountId> {
+        match self {
+            Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),
+            _ => None,
+        }
+    }
+
+    pub fn confirmed(&self) -> bool {
+        matches!(self, Self::Confirmed(_))
+    }
+}
+
+impl<T> Default for SponsorshipState<T> {
+    fn default() -> Self {
+        Self::Disabled
+    }
+}
+
+#[derive(Encode, Decode, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct Collection<T: frame_system::Config> {
+    pub owner: T::AccountId,
+    pub mode: CollectionMode,
+    pub access: AccessMode,
+    pub decimal_points: DecimalPoints,
+    pub name: Vec<u16>,        // 64 include null escape char
+    pub description: Vec<u16>, // 256 include null escape char
+    pub token_prefix: Vec<u8>, // 16 include null escape char
+    pub mint_mode: bool,
+    pub offchain_schema: Vec<u8>,
+    pub schema_version: SchemaVersion,
+    pub sponsorship: SponsorshipState<T::AccountId>,
+    pub limits: CollectionLimits<T::BlockNumber>, // Collection private restrictions 
+    pub variable_on_chain_schema: Vec<u8>, //
+    pub const_on_chain_schema: Vec<u8>, //
+}
+
+pub struct CollectionHandle<T: frame_system::Config> {
+    pub id: CollectionId,
+    pub collection: Collection<T>,
+}
+
+impl<T: frame_system::Config> Deref for CollectionHandle<T> {
+    type Target = Collection<T>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.collection
+    }
+}
+
+impl<T: frame_system::Config> DerefMut for CollectionHandle<T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.collection
+    }
+}
+
+#[derive(Encode, Decode, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct NftItemType<AccountId> {
+    pub owner: AccountId,
+    pub const_data: Vec<u8>,
+    pub variable_data: Vec<u8>,
+}
+
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct FungibleItemType {
+    pub value: u128,
+}
+
+#[derive(Encode, Decode, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct ReFungibleItemType<AccountId> {
+    pub owner: Vec<Ownership<AccountId>>,
+    pub const_data: Vec<u8>,
+    pub variable_data: Vec<u8>,
+}
+
+
+#[derive(Encode, Decode, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct CollectionLimits<BlockNumber: Encode + Decode> {
+    pub account_token_ownership_limit: u32,
+    pub sponsored_data_size: u32,
+    /// None - setVariableMetadata is not sponsored
+    /// Some(v) - setVariableMetadata is sponsored 
+    ///           if there is v block between txs
+    pub sponsored_data_rate_limit: Option<BlockNumber>,
+    pub token_limit: u32,
+
+    // Timeouts for item types in passed blocks
+    pub sponsor_transfer_timeout: u32,
+    pub owner_can_transfer: bool,
+    pub owner_can_destroy: bool,
+}
+
+impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {
+    fn default() -> Self {
+        Self { 
+            account_token_ownership_limit: 10_000_000, 
+            token_limit: u32::max_value(),
+            sponsored_data_size: u32::MAX, 
+            sponsored_data_rate_limit: None,
+            sponsor_transfer_timeout: 14400,
+            owner_can_transfer: true,
+            owner_can_destroy: true
+        }
+    }
+}
+
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct ChainLimits {
+    pub collection_numbers_limit: u32,
+    pub account_token_ownership_limit: u32,
+    pub collections_admins_limit: u64,
+    pub custom_data_limit: u32,
+
+    // Timeouts for item types in passed blocks
+    pub nft_sponsor_transfer_timeout: u32,
+    pub fungible_sponsor_transfer_timeout: u32,
+    pub refungible_sponsor_transfer_timeout: u32,
+
+    // Schema limits
+    pub offchain_schema_limit: u32,
+    pub variable_on_chain_schema_limit: u32,
+    pub const_on_chain_schema_limit: u32,
+}
+
+
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct CreateNftData {
+    pub const_data: Vec<u8>,
+    pub variable_data: Vec<u8>,
+}
+
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct CreateFungibleData {
+    pub value: u128,
+}
+
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct CreateReFungibleData {
+    pub const_data: Vec<u8>,
+    pub variable_data: Vec<u8>,
+    pub pieces: u128,
+}
+
+#[derive(Encode, Decode, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub enum CreateItemData {
+    NFT(CreateNftData),
+    Fungible(CreateFungibleData),
+    ReFungible(CreateReFungibleData),
+}
+
+impl CreateItemData {
+    pub fn len(&self) -> usize {
+        let len = match self {
+            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),
+            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),
+            _ => 0
+        };
+        
+        return len;
+    }
+}
+
+impl From<CreateNftData> for CreateItemData {
+    fn from(item: CreateNftData) -> Self {
+        CreateItemData::NFT(item)
+    }
+}
+
+impl From<CreateReFungibleData> for CreateItemData {
+    fn from(item: CreateReFungibleData) -> Self {
+        CreateItemData::ReFungible(item)
+    }
+}
+
+impl From<CreateFungibleData> for CreateItemData {
+    fn from(item: CreateFungibleData) -> Self {
+        CreateItemData::Fungible(item)
+    }
+}
\ No newline at end of file
modifiedruntime/Cargo.tomldiffbeforeafterboth
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -26,7 +26,11 @@
 
 # local dependencies
 pallet-nft = { path = '../pallets/nft', default-features = false, version = '3.0.0' }
+nft-data-structs = { path = '../primitives', default-features = false }
 pallet-inflation = { path = '../pallets/inflation', default-features = false, version = '3.0.0' }
+pallet-scheduler = { path = '../pallets/scheduler', default-features = false, version = '3.0.0' }
+pallet-nft-transaction-payment = { path = '../pallets/nft-transaction-payment', default-features = false, version = '3.0.0' }
+pallet-nft-charge-transaction = { path = '../pallets/nft-charge-transaction', default-features = false, version = '3.0.0' }
 
 # Substrate dependencies
 frame-benchmarking = { default-features = false, optional = true, version = '3.0.0' }
@@ -96,9 +100,12 @@
     'pallet-transaction-payment-rpc-runtime-api/std',
     'pallet-treasury/std',
     'pallet-vesting/std',
-
     'pallet-inflation/std',
     'pallet-nft/std',
+    'pallet-scheduler/std',
+    'pallet-nft-charge-transaction/std',
+    'pallet-nft-transaction-payment/std', 
+    'nft-data-structs/std',
     'sp-api/std',
     'sp-block-builder/std',
     'sp-consensus-aura/std',
modifiedruntime/src/chain_extension.rsdiffbeforeafterboth
--- a/runtime/src/chain_extension.rs
+++ b/runtime/src/chain_extension.rs
@@ -17,7 +17,8 @@
 
 extern crate pallet_nft;
 pub use pallet_nft::*;
-use crate::Runtime;
+use nft_data_structs::*;
+// use crate::Runtime;
 use sp_runtime::AccountId32;
 use crate::Vec;
 
@@ -41,7 +42,7 @@
 /// The chain Extension of NFT pallet
 pub struct NFTExtension;
 
-impl<C: Config> ChainExtension<C> for NFTExtension {
+impl<C: Config + pallet_contracts::Config> ChainExtension<C> for NFTExtension {
     fn call<E: Ext>(func_id: u32, env: Environment<E, InitState>) -> Result<RetVal, DispatchError>
     where
         E: Ext<T = C>,
modifiedruntime/src/lib.rsdiffbeforeafterboth
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -24,7 +24,7 @@
     create_runtime_str, generic, impl_opaque_keys,
     traits::{
         Convert, ConvertInto, BlakeTwo256, Block as BlockT, IdentifyAccount, 
-        IdentityLookup, NumberFor, Verify, AccountIdConversion,
+		IdentityLookup, NumberFor, Verify, AccountIdConversion,
     },
     transaction_validity::{TransactionSource, TransactionValidity},
     ApplyExtrinsicResult, MultiSignature,
@@ -51,17 +51,28 @@
         WeightToFeePolynomial, WeightToFeeCoefficient, WeightToFeeCoefficients
     },
 };
+use pallet_nft_transaction_payment::*;
+use pallet_nft_charge_transaction::*;
+use nft_data_structs::*;
 use pallet_contracts::weights::WeightInfo;
 // #[cfg(any(feature = "std", test))]
 use frame_system::{
     self as system,
-    EnsureRoot, 
+    EnsureRoot, EnsureSigned,
 	limits::{BlockWeights, BlockLength},
 };
 use sp_std::{prelude::*, marker::PhantomData};
 use sp_arithmetic::{traits::{BaseArithmetic, Unsigned}};
 use smallvec::smallvec;
 
+use sp_runtime::{
+	traits::{ 
+		Dispatchable,
+	},
+};
+use pallet_contracts::chain_extension::UncheckedFrom;
+
+
 pub use pallet_timestamp::Call as TimestampCall;
 
 mod chain_extension;
@@ -173,45 +184,8 @@
         runtime_version: VERSION,
         can_author_with: Default::default(),
     }
-}
-
-/// Provides a membership set with only the configured aura users
-pub struct ValiudatorsOnly<Runtime: pallet_aura::Config>(PhantomData<Runtime>);
-impl frame_support::traits::Contains<AccountId> for ValiudatorsOnly<Runtime> {
-	fn contains(t: &AccountId) -> bool {
-        let arr: [u8; 32] = *t.as_ref();
-        let raw_key: Vec<u8> = Vec::from(arr);
-
-        match pallet_aura::Module::<Runtime>::authorities().iter().find(|auth| auth.to_raw_vec() == raw_key) {
-            Some(_) => true,
-            None => false,
-        }  
-	}
-	fn sorted_members() -> Vec<AccountId> {
-        let mut members: Vec<AccountId> = Vec::new();
-        for auth in pallet_aura::Module::<Runtime>::authorities() {
-            let mut arr: [u8; 32] = Default::default(); 
-            let bor_arr = auth.clone().to_raw_vec();
-            let slice = bor_arr.as_slice();
-            arr.copy_from_slice(slice);
-            members.push(AccountId::from(arr));
-        }
-        members  
-	}
-	fn count() -> usize {
-        pallet_aura::Module::<Runtime>::authorities().len()
-	}
 }
 
-impl frame_support::traits::ContainsLengthBound for ValiudatorsOnly<Runtime> {
-	fn min_len() -> usize {
-		1
-	}
-	fn max_len() -> usize {
-		100
-	}
-}
-
 type NegativeImbalance = <Balances as Currency<AccountId>>::NegativeImbalance;
 
 pub struct DealWithFees;
@@ -229,21 +203,6 @@
 		}
 	}
 }
-
-// impl OnUnbalanced<NegativeImbalance> for DealWithFees {
-// 	fn on_unbalanceds<B>(mut fees_then_tips: impl Iterator<Item=NegativeImbalance>) {
-// 		if let Some(fees) = fees_then_tips.next() {
-// 			// for fees, 100% to treasury
-// 			let mut split = fees.ration(100, 0);
-// 			if let Some(tips) = fees_then_tips.next() {
-// 				// for tips, if any, 100% to treasury
-// 				tips.ration_merge_into(100, 0, &mut split);
-// 			}
-// 			Treasury::on_unbalanced(split.0);
-// 			// Author::on_unbalanced(split.1);
-// 		}
-// 	}
-// }
 
 /// We assume that ~10% of the block weight is consumed by `on_initalize` handlers.
 /// This is used to limit the maximal weight of a single extrinsic.
@@ -549,6 +508,46 @@
 	type InflationBlockInterval = InflationBlockInterval;
 }
 
+
+parameter_types! {
+	pub MaximumSchedulerWeight: Weight = Perbill::from_percent(50) *
+		RuntimeBlockWeights::get().max_block;
+	pub const MaxScheduledPerBlock: u32 = 50;
+}
+
+pub struct Sponsoring;
+impl SponsoringResolve<AccountId, Call> for Sponsoring {
+
+	fn resolve(who: &AccountId, call: &Call) -> Option<AccountId> 
+	where 
+		Call: Dispatchable<Info=DispatchInfo>,
+		Call: IsSubType<pallet_nft::Call<Runtime>>, 
+		Call: IsSubType<pallet_contracts::Call<Runtime>>,
+		AccountId: AsRef<[u8]>,
+		AccountId: UncheckedFrom<Hash>
+	{
+		pallet_nft_transaction_payment::Module::<Runtime>::withdraw_type(who, call)
+	}
+}
+
+impl pallet_scheduler::Config for Runtime {
+	type Event = Event;
+	type Origin = Origin;
+	type PalletsOrigin = OriginCaller;
+	type Call = Call;
+	type MaximumWeight = MaximumSchedulerWeight;
+	type ScheduleOrigin = EnsureSigned<AccountId>;
+	type MaxScheduledPerBlock = MaxScheduledPerBlock;
+	type Sponsoring = Sponsoring;
+	type WeightInfo = ();
+}
+
+impl pallet_nft_transaction_payment::Config for Runtime {
+}
+
+impl pallet_nft_charge_transaction::Config for Runtime {
+}
+
 construct_runtime!(
     pub enum Runtime where
         Block = Block,
@@ -564,10 +563,13 @@
         Balances: pallet_balances::{Module, Call, Storage, Config<T>, Event<T>},
         TransactionPayment: pallet_transaction_payment::{Module, Storage},
         Sudo: pallet_sudo::{Module, Call, Config<T>, Storage, Event<T>},
-        Inflation: pallet_inflation::{Module, Call, Storage},
+		Inflation: pallet_inflation::{Module, Call, Storage},
+		Scheduler: pallet_scheduler::{Module, Call, Storage, Event<T>},
         Nft: pallet_nft::{Module, Call, Config<T>, Storage, Event<T>},
         Treasury: pallet_treasury::{Module, Call, Storage, Config, Event<T>},
-        Vesting: pallet_vesting::{Module, Call, Config<T>, Storage, Event<T>},
+		Vesting: pallet_vesting::{Module, Call, Config<T>, Storage, Event<T>},
+		NftPayment: pallet_nft_transaction_payment::{Module, Call, Storage},
+		Charging: pallet_nft_charge_transaction::{Module, Call, Storage },
     }
 );
 
@@ -589,7 +591,7 @@
     system::CheckEra<Runtime>,
     system::CheckNonce<Runtime>,
     system::CheckWeight<Runtime>,
-    pallet_nft::ChargeTransactionPayment<Runtime>,
+    pallet_nft_charge_transaction::ChargeTransactionPayment<Runtime>,
 );
 /// Unchecked extrinsic type as expected by this runtime.
 pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;