git.delta.rocks / unique-network / refs/commits / 2a441390261c

difftreelog

Scheduler refactored and fixed

str-mv2021-06-15parent: #eb5b535.patch.diff
in: master

8 files changed

modifiedpallets/nft-charge-transaction/src/lib.rsdiffbeforeafterboth
--- a/pallets/nft-charge-transaction/src/lib.rs
+++ b/pallets/nft-charge-transaction/src/lib.rs
@@ -135,6 +135,13 @@
 			.map(|i| (fee, i));
         }
 
+        // check errors
+        let error = <pallet_nft_transaction_payment::Module<T>>::check_error(who, call);
+        match error {
+            Err(error) => return Err(error),
+            Ok(error) => {}
+        };
+
         // 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);
addedpallets/nft-transaction-payment/README.mddiffbeforeafterboth
--- /dev/null
+++ b/pallets/nft-transaction-payment/README.md
@@ -0,0 +1,13 @@
+# Nft Transaction Payment
+
+## Overview
+
+A module containing the sponsoring logic for paying for sponsored collections
+
+**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.
modifiedpallets/nft-transaction-payment/src/lib.rsdiffbeforeafterboth
--- a/pallets/nft-transaction-payment/src/lib.rs
+++ b/pallets/nft-transaction-payment/src/lib.rs
@@ -31,6 +31,9 @@
 	traits::{ 
 		Hash, Dispatchable,
 	},
+	transaction_validity::{
+        InvalidTransaction, TransactionValidityError,
+    },
 };
 use pallet_contracts::chain_extension::UncheckedFrom;
 use sp_std::prelude::*;
@@ -85,6 +88,36 @@
 impl<T: Config> Module<T> 
 {
 
+	pub fn check_error(
+		who: &T::AccountId,
+		call: &T::Call
+	) -> Result<bool, TransactionValidityError> 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>
+		{
+
+			match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {
+				Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {
+	
+					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 Err(InvalidTransaction::Call.into());
+						}
+					}
+					Ok(true)
+				},
+				_ => { Ok(true) },
+			}
+		}
+
 	pub fn withdraw_type(
 		who: &T::AccountId,
 		call: &T::Call
@@ -366,160 +399,4 @@
 
 		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
+}
\ No newline at end of file
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::*;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}
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 core::ops::{Deref, DerefMut};36use nft_data_structs::{37    MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,38	AccessMode, ChainLimits, Collection, CreateItemData, CollectionLimits,39    CollectionId, CollectionMode, TokenId, 40    SchemaVersion, SponsorshipState, Ownership,41    NftItemType, FungibleItemType, ReFungibleItemType42};4344#[cfg(test)]45mod mock;4647#[cfg(test)]48mod tests;4950mod default_weights;5152#[cfg(feature = "runtime-benchmarks")]53mod benchmarking;5455pub trait WeightInfo {56	fn create_collection() -> Weight;57	fn destroy_collection() -> Weight;58	fn add_to_white_list() -> Weight;59	fn remove_from_white_list() -> Weight;60    fn set_public_access_mode() -> Weight;61    fn set_mint_permission() -> Weight;62    fn change_collection_owner() -> Weight;63    fn add_collection_admin() -> Weight;64    fn remove_collection_admin() -> Weight;65    fn set_collection_sponsor() -> Weight;66    fn confirm_sponsorship() -> Weight;67    fn remove_collection_sponsor() -> Weight;68    fn create_item(s: usize) -> Weight;69    fn burn_item() -> Weight;70    fn transfer() -> Weight;71    fn approve() -> Weight;72    fn transfer_from() -> Weight;73    fn set_offchain_schema() -> Weight;74    fn set_const_on_chain_schema() -> Weight;75    fn set_variable_on_chain_schema() -> Weight;76    fn set_variable_meta_data() -> Weight;77    fn enable_contract_sponsoring() -> Weight;78    fn set_schema_version() -> Weight;79    fn set_chain_limits() -> Weight;80    fn set_contract_sponsoring_rate_limit() -> Weight;81    fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;82    fn toggle_contract_white_list() -> Weight;83    fn add_to_contract_white_list() -> Weight;84    fn remove_from_contract_white_list() -> Weight;85    fn set_collection_limits() -> Weight;86}8788decl_error! {89	/// Error for non-fungible-token module.90	pub enum Error for Module<T: Config> {91        /// Total collections bound exceeded.92        TotalCollectionsLimitExceeded,93		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.94        CollectionDecimalPointLimitExceeded, 95        /// Collection name can not be longer than 63 char.96        CollectionNameLimitExceeded, 97        /// Collection description can not be longer than 255 char.98        CollectionDescriptionLimitExceeded, 99        /// Token prefix can not be longer than 15 char.100        CollectionTokenPrefixLimitExceeded,101        /// This collection does not exist.102        CollectionNotFound,103        /// Item not exists.104        TokenNotFound,105        /// Admin not found106        AdminNotFound,107        /// Arithmetic calculation overflow.108        NumOverflow,       109        /// Account already has admin role.110        AlreadyAdmin,  111        /// You do not own this collection.112        NoPermission,113        /// This address is not set as sponsor, use setCollectionSponsor first.114        ConfirmUnsetSponsorFail,115        /// Collection is not in mint mode.116        PublicMintingNotAllowed,117        /// Sender parameter and item owner must be equal.118        MustBeTokenOwner,119        /// Item balance not enough.120        TokenValueTooLow,121        /// Size of item is too large.122        NftSizeLimitExceeded,123        /// No approve found124        ApproveNotFound,125        /// Requested value more than approved.126        TokenValueNotEnough,127        /// Only approved addresses can call this method.128        ApproveRequired,129        /// Address is not in white list.130        AddresNotInWhiteList,131        /// Number of collection admins bound exceeded.132        CollectionAdminsLimitExceeded,133        /// Owned tokens by a single address bound exceeded.134        AddressOwnershipLimitExceeded,135        /// Length of items properties must be greater than 0.136        EmptyArgument,137        /// const_data exceeded data limit.138        TokenConstDataLimitExceeded,139        /// variable_data exceeded data limit.140        TokenVariableDataLimitExceeded,141        /// Not NFT item data used to mint in NFT collection.142        NotNftDataUsedToMintNftCollectionToken,143        /// Not Fungible item data used to mint in Fungible collection.144        NotFungibleDataUsedToMintFungibleCollectionToken,145        /// Not Re Fungible item data used to mint in Re Fungible collection.146        NotReFungibleDataUsedToMintReFungibleCollectionToken,147        /// Unexpected collection type.148        UnexpectedCollectionType,149        /// Can't store metadata in fungible tokens.150        CantStoreMetadataInFungibleTokens,151        /// Collection token limit exceeded152        CollectionTokenLimitExceeded,153        /// Account token limit exceeded per collection154        AccountTokenLimitExceeded,155        /// Collection limit bounds per collection exceeded156        CollectionLimitBoundsExceeded,157        /// Tried to enable permissions which are only permitted to be disabled158        OwnerPermissionsCantBeReverted,159        /// Schema data size limit bound exceeded160        SchemaDataLimitExceeded,161        /// Maximum refungibility exceeded162        WrongRefungiblePieces,163        /// createRefungible should be called with one owner164        BadCreateRefungibleCall,165	}166}167168pub struct CollectionHandle<T: frame_system::Config> {169    pub id: CollectionId,170    pub collection: Collection<T>,171}172173impl<T: frame_system::Config> Deref for CollectionHandle<T> {174    type Target = Collection<T>;175176    fn deref(&self) -> &Self::Target {177        &self.collection178    }179}180181impl<T: frame_system::Config> DerefMut for CollectionHandle<T> {182    fn deref_mut(&mut self) -> &mut Self::Target {183        &mut self.collection184    }185}186187188189pub trait Config: system::Config + Sized {190    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;191192    /// Weight information for extrinsics in this pallet.193	type WeightInfo: WeightInfo;194195    type Currency: Currency<Self::AccountId>;196    type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;197    type TreasuryAccountId: Get<Self::AccountId>;198}199200// # Used definitions201//202// ## User control levels203//204// chain-controlled - key is uncontrolled by user205//                    i.e autoincrementing index206//                    can use non-cryptographic hash207// real - key is controlled by user208//        but it is hard to generate enough colliding values, i.e owner of signed txs209//        can use non-cryptographic hash210// controlled - key is completly controlled by users211//              i.e maps with mutable keys212//              should use cryptographic hash213//214// ## User control level downgrade reasons215//216// ?1 - chain-controlled -> controlled217//      collections/tokens can be destroyed, resulting in massive holes218// ?2 - chain-controlled -> controlled219//      same as ?1, but can be only added, resulting in easier exploitation220// ?3 - real -> controlled221//      no confirmation required, so addresses can be easily generated222decl_storage! {223    trait Store for Module<T: Config> as Nft {224225        //#region Private members226        /// Id of next collection227        CreatedCollectionCount: u32;228        /// Used for migrations229        ChainVersion: u64;230        /// Id of last collection token231        /// Collection id (controlled?1)232        ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;233        //#endregion234235        //#region Chain limits struct236        pub ChainLimit get(fn chain_limit) config(): ChainLimits;237        //#endregion238239        //#region Bound counters240        /// Amount of collections destroyed, used for total amount tracking with241        /// CreatedCollectionCount242        DestroyedCollectionCount: u32;243        /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)244        /// Account id (real)245        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;246        //#endregion247248        //#region Basic collections249        /// Collection info250        /// Collection id (controlled?1)251        pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;252        /// List of collection admins253        /// Collection id (controlled?2)254        pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::AccountId>;255        /// Whitelisted collection users256        /// Collection id (controlled?2), user id (controlled?3)257        pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;258        //#endregion259260        /// How many of collection items user have261        /// Collection id (controlled?2), account id (real)262        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;263264        /// Amount of items which spender can transfer out of owners account (via transferFrom)265        /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))266        /// TODO: Off chain worker should remove from this map when token gets removed267        pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;268269        //#region Item collections270        /// Collection id (controlled?2), token id (controlled?1)271        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::AccountId>>;272        /// Collection id (controlled?2), owner (controlled?2)273        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;274        /// Collection id (controlled?2), token id (controlled?1)275        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::AccountId>>;276        //#endregion277278        //#region Index list279        /// Collection id (controlled?2), tokens owner (controlled?2)280        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;281        //#endregion282283        //#region Tokens transfer rate limit baskets284        /// (Collection id (controlled?2), who created (real))285        /// TODO: Off chain worker should remove from this map when collection gets removed286        pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;287        /// Collection id (controlled?2), token id (controlled?2)288        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;289        /// Collection id (controlled?2), owning user (real)290        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;291        /// Collection id (controlled?2), token id (controlled?2)292        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;293        //#endregion294295        /// Variable metadata sponsoring296        /// Collection id (controlled?2), token id (controlled?2)297        pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;298      299        //#region Contract Sponsorship and Ownership300        /// Contract address (real)301        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;302        /// Contract address (real)303        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;304        /// (Contract address(real), caller (real))305        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;306        /// Contract address (real)307        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;308        /// Contract address (real)309        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 310        /// Contract address (real) => Whitelisted user (controlled?3)311        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 312        //#endregion313    }314    add_extra_genesis {315        build(|config: &GenesisConfig<T>| {316            // Modification of storage317            for (_num, _c) in &config.collection_id {318                <Module<T>>::init_collection(_c);319            }320321            for (_num, _c, _i) in &config.nft_item_id {322                <Module<T>>::init_nft_token(*_c, _i);323            }324325            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {326                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);327            }328329            for (_num, _c, _i) in &config.refungible_item_id {330                <Module<T>>::init_refungible_token(*_c, _i);331            }332        })333    }334}335336decl_event!(337    pub enum Event<T>338    where339        AccountId = <T as system::Config>::AccountId,340    {341        /// New collection was created342        /// 343        /// # Arguments344        /// 345        /// * collection_id: Globally unique identifier of newly created collection.346        /// 347        /// * mode: [CollectionMode] converted into u8.348        /// 349        /// * account_id: Collection owner.350        CollectionCreated(CollectionId, u8, AccountId),351352        /// New item was created.353        /// 354        /// # Arguments355        /// 356        /// * collection_id: Id of the collection where item was created.357        /// 358        /// * item_id: Id of an item. Unique within the collection.359        ///360        /// * recipient: Owner of newly created item 361        ItemCreated(CollectionId, TokenId, AccountId),362363        /// Collection item was burned.364        /// 365        /// # Arguments366        /// 367        /// collection_id.368        /// 369        /// item_id: Identifier of burned NFT.370        ItemDestroyed(CollectionId, TokenId),371372        /// Item was transferred373        ///374        /// * collection_id: Id of collection to which item is belong375        ///376        /// * item_id: Id of an item377        ///378        /// * sender: Original owner of item379        ///380        /// * recipient: New owner of item381        ///382        /// * amount: Always 1 for NFT383        Transfer(CollectionId, TokenId, AccountId, AccountId, u128),384385        /// * collection_id386        ///387        /// * item_id388        ///389        /// * sender390        ///391        /// * spender392        ///393        /// * amount394        Approved(CollectionId, TokenId, AccountId, AccountId, u128),395    }396);397398decl_module! {399    pub struct Module<T: Config> for enum Call 400    where 401        origin: T::Origin402    {403        fn deposit_event() = default;404        type Error = Error<T>;405406        fn on_initialize(_now: T::BlockNumber) -> Weight {407            0408        }409410        /// 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.411        /// 412        /// # Permissions413        /// 414        /// * Anyone.415        /// 416        /// # Arguments417        /// 418        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.419        /// 420        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.421        /// 422        /// * token_prefix: UTF-8 string with token prefix.423        /// 424        /// * mode: [CollectionMode] collection type and type dependent data.425        // returns collection ID426        #[weight = <T as Config>::WeightInfo::create_collection()]427        #[transactional]428        pub fn create_collection(origin,429                                 collection_name: Vec<u16>,430                                 collection_description: Vec<u16>,431                                 token_prefix: Vec<u8>,432                                 mode: CollectionMode) -> DispatchResult {433434            // Anyone can create a collection435            let who = ensure_signed(origin)?;436437            // Take a (non-refundable) deposit of collection creation438            let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();439            imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(440                &T::TreasuryAccountId::get(),441                T::CollectionCreationPrice::get(),442            ));443            <T as Config>::Currency::settle(444                &who,445                imbalance,446                WithdrawReasons::TRANSFER,447                ExistenceRequirement::KeepAlive,448            ).map_err(|_| Error::<T>::NoPermission)?;449450            let decimal_points = match mode {451                CollectionMode::Fungible(points) => points,452                _ => 0453            };454455            let chain_limit = ChainLimit::get();456457            let created_count = CreatedCollectionCount::get();458            let destroyed_count = DestroyedCollectionCount::get();459460            // bound Total number of collections461            ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);462463            // check params464            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);465            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);466            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);467            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);468469            // Generate next collection ID470            let next_id = created_count471                .checked_add(1)472                .ok_or(Error::<T>::NumOverflow)?;473474            CreatedCollectionCount::put(next_id);475476            let limits = CollectionLimits {477                sponsored_data_size: chain_limit.custom_data_limit,478                ..Default::default()479            };480481            // Create new collection482            let new_collection = Collection {483                owner: who.clone(),484                name: collection_name,485                mode: mode.clone(),486                mint_mode: false,487                access: AccessMode::Normal,488                description: collection_description,489                decimal_points: decimal_points,490                token_prefix: token_prefix,491                offchain_schema: Vec::new(),492                schema_version: SchemaVersion::ImageURL,493                sponsorship: SponsorshipState::Disabled,494                variable_on_chain_schema: Vec::new(),495                const_on_chain_schema: Vec::new(),496                limits,497            };498499            // Add new collection to map500            <CollectionById<T>>::insert(next_id, new_collection);501502            // call event503            Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who.clone()));504505            Ok(())506        }507508        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.509        /// 510        /// # Permissions511        /// 512        /// * Collection Owner.513        /// 514        /// # Arguments515        /// 516        /// * collection_id: collection to destroy.517        #[weight = <T as Config>::WeightInfo::destroy_collection()]518        #[transactional]519        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {520521            let sender = ensure_signed(origin)?;522            let collection = Self::get_collection(collection_id)?;523            Self::check_owner_permissions(&collection, sender)?;524            if !collection.limits.owner_can_destroy {525                fail!(Error::<T>::NoPermission);526            }527528            <AddressTokens<T>>::remove_prefix(collection_id);529            <Allowances<T>>::remove_prefix(collection_id);530            <Balance<T>>::remove_prefix(collection_id);531            <ItemListIndex>::remove(collection_id);532            <AdminList<T>>::remove(collection_id);533            <CollectionById<T>>::remove(collection_id);534            <WhiteList<T>>::remove_prefix(collection_id);535536            <NftItemList<T>>::remove_prefix(collection_id);537            <FungibleItemList<T>>::remove_prefix(collection_id);538            <ReFungibleItemList<T>>::remove_prefix(collection_id);539540            <NftTransferBasket<T>>::remove_prefix(collection_id);541            <FungibleTransferBasket<T>>::remove_prefix(collection_id);542            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);543544            <VariableMetaDataBasket<T>>::remove_prefix(collection_id);545546            DestroyedCollectionCount::put(DestroyedCollectionCount::get()547                .checked_add(1)548                .ok_or(Error::<T>::NumOverflow)?);549550            Ok(())551        }552553        /// Add an address to white list.554        /// 555        /// # Permissions556        /// 557        /// * Collection Owner558        /// * Collection Admin559        /// 560        /// # Arguments561        /// 562        /// * collection_id.563        /// 564        /// * address.565        #[weight = <T as Config>::WeightInfo::add_to_white_list()]566        #[transactional]567        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{568569            let sender = ensure_signed(origin)?;570            let collection = Self::get_collection(collection_id)?;571            Self::check_owner_or_admin_permissions(&collection, sender)?;572573            <WhiteList<T>>::insert(collection_id, address, true);574            575            Ok(())576        }577578        /// Remove an address from white list.579        /// 580        /// # Permissions581        /// 582        /// * Collection Owner583        /// * Collection Admin584        /// 585        /// # Arguments586        /// 587        /// * collection_id.588        /// 589        /// * address.590        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]591        #[transactional]592        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{593594            let sender = ensure_signed(origin)?;595            let collection = Self::get_collection(collection_id)?;596            Self::check_owner_or_admin_permissions(&collection, sender)?;597598            <WhiteList<T>>::remove(collection_id, address);599600            Ok(())601        }602603        /// Toggle between normal and white list access for the methods with access for `Anyone`.604        /// 605        /// # Permissions606        /// 607        /// * Collection Owner.608        /// 609        /// # Arguments610        /// 611        /// * collection_id.612        /// 613        /// * mode: [AccessMode]614        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]615        #[transactional]616        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult617        {618            let sender = ensure_signed(origin)?;619620            let mut target_collection = Self::get_collection(collection_id)?;621            Self::check_owner_permissions(&target_collection, sender)?;622            target_collection.access = mode;623            Self::save_collection(target_collection);624625            Ok(())626        }627628        /// Allows Anyone to create tokens if:629        /// * White List is enabled, and630        /// * Address is added to white list, and631        /// * This method was called with True parameter632        /// 633        /// # Permissions634        /// * Collection Owner635        ///636        /// # Arguments637        /// 638        /// * collection_id.639        /// 640        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.641        #[weight = <T as Config>::WeightInfo::set_mint_permission()]642        #[transactional]643        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult644        {645            let sender = ensure_signed(origin)?;646647            let mut target_collection = Self::get_collection(collection_id)?;648            Self::check_owner_permissions(&target_collection, sender)?;649            target_collection.mint_mode = mint_permission;650            Self::save_collection(target_collection);651652            Ok(())653        }654655        /// Change the owner of the collection.656        /// 657        /// # Permissions658        /// 659        /// * Collection Owner.660        /// 661        /// # Arguments662        /// 663        /// * collection_id.664        /// 665        /// * new_owner.666        #[weight = <T as Config>::WeightInfo::change_collection_owner()]667        #[transactional]668        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {669670            let sender = ensure_signed(origin)?;671            let mut target_collection = Self::get_collection(collection_id)?;672            Self::check_owner_permissions(&target_collection, sender)?;673            target_collection.owner = new_owner;674            Self::save_collection(target_collection);675676            Ok(())677        }678679        /// Adds an admin of the Collection.680        /// 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. 681        /// 682        /// # Permissions683        /// 684        /// * Collection Owner.685        /// * Collection Admin.686        /// 687        /// # Arguments688        /// 689        /// * collection_id: ID of the Collection to add admin for.690        /// 691        /// * new_admin_id: Address of new admin to add.692        #[weight = <T as Config>::WeightInfo::add_collection_admin()]693        #[transactional]694        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {695696            let sender = ensure_signed(origin)?;697            let collection = Self::get_collection(collection_id)?;698            Self::check_owner_or_admin_permissions(&collection, sender)?;699            let mut admin_arr = <AdminList<T>>::get(collection_id);700701            match admin_arr.binary_search(&new_admin_id) {702                Ok(_) => {},703                Err(idx) => {704                    let limits = ChainLimit::get();705                    ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);706                    admin_arr.insert(idx, new_admin_id);707                    <AdminList<T>>::insert(collection_id, admin_arr);708                }709            }710            Ok(())711        }712713        /// 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.714        ///715        /// # Permissions716        /// 717        /// * Collection Owner.718        /// * Collection Admin.719        /// 720        /// # Arguments721        /// 722        /// * collection_id: ID of the Collection to remove admin for.723        /// 724        /// * account_id: Address of admin to remove.725        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]726        #[transactional]727        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {728729            let sender = ensure_signed(origin)?;730            let collection = Self::get_collection(collection_id)?;731            Self::check_owner_or_admin_permissions(&collection, sender)?;732            let mut admin_arr = <AdminList<T>>::get(collection_id);733734            match admin_arr.binary_search(&account_id) {735                Ok(idx) => {736                    admin_arr.remove(idx);737                    <AdminList<T>>::insert(collection_id, admin_arr);738                },739                Err(_) => {}740            }741            Ok(())742        }743744        /// # Permissions745        /// 746        /// * Collection Owner747        /// 748        /// # Arguments749        /// 750        /// * collection_id.751        /// 752        /// * new_sponsor.753        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]754        #[transactional]755        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {756757            let sender = ensure_signed(origin)?;758            let mut target_collection = Self::get_collection(collection_id)?;759            Self::check_owner_permissions(&target_collection, sender)?;760761            target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);762            Self::save_collection(target_collection);763764            Ok(())765        }766767        /// # Permissions768        /// 769        /// * Sponsor.770        /// 771        /// # Arguments772        /// 773        /// * collection_id.774        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]775        #[transactional]776        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {777778            let sender = ensure_signed(origin)?;779780            let mut target_collection = Self::get_collection(collection_id)?;781            ensure!(782                target_collection.sponsorship.pending_sponsor() == Some(&sender),783                Error::<T>::ConfirmUnsetSponsorFail784            );785786            target_collection.sponsorship = SponsorshipState::Confirmed(sender);787            Self::save_collection(target_collection);788789            Ok(())790        }791792        /// Switch back to pay-per-own-transaction model.793        ///794        /// # Permissions795        ///796        /// * Collection owner.797        /// 798        /// # Arguments799        /// 800        /// * collection_id.801        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]802        #[transactional]803        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {804805            let sender = ensure_signed(origin)?;806807            let mut target_collection = Self::get_collection(collection_id)?;808            Self::check_owner_permissions(&target_collection, sender)?;809810            target_collection.sponsorship = SponsorshipState::Disabled;811            Self::save_collection(target_collection);812813            Ok(())814        }815816        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.817        /// 818        /// # Permissions819        /// 820        /// * Collection Owner.821        /// * Collection Admin.822        /// * Anyone if823        ///     * White List is enabled, and824        ///     * Address is added to white list, and825        ///     * MintPermission is enabled (see SetMintPermission method)826        /// 827        /// # Arguments828        /// 829        /// * collection_id: ID of the collection.830        /// 831        /// * owner: Address, initial owner of the NFT.832        ///833        /// * data: Token data to store on chain.834        // #[weight =835        // (130_000_000 as Weight)836        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))837        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))838        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]839840        #[weight = <T as Config>::WeightInfo::create_item(data.len())]841        #[transactional]842        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {843            let sender = ensure_signed(origin)?;844            Self::create_item_internal(sender, collection_id, owner, data)845        }846847        /// This method creates multiple items in a collection created with CreateCollection method.848        /// 849        /// # Permissions850        /// 851        /// * Collection Owner.852        /// * Collection Admin.853        /// * Anyone if854        ///     * White List is enabled, and855        ///     * Address is added to white list, and856        ///     * MintPermission is enabled (see SetMintPermission method)857        /// 858        /// # Arguments859        /// 860        /// * collection_id: ID of the collection.861        /// 862        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].863        /// 864        /// * owner: Address, initial owner of the NFT.865        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()866                               .map(|data| { data.len() })867                               .sum())]868        #[transactional]869        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {870871            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);872            let sender = ensure_signed(origin)?;873874            let target_collection = Self::get_collection(collection_id)?;875876            Self::can_create_items_in_collection(&target_collection, &sender, &owner, items_data.len() as u32)?;877878            for data in &items_data {879                Self::validate_create_item_args(&target_collection, data)?;880            }881            for data in &items_data {882                Self::create_item_no_validation(&target_collection, owner.clone(), data.clone())?;883            }884885            Ok(())886        }887888        /// Destroys a concrete instance of NFT.889        /// 890        /// # Permissions891        /// 892        /// * Collection Owner.893        /// * Collection Admin.894        /// * Current NFT Owner.895        /// 896        /// # Arguments897        /// 898        /// * collection_id: ID of the collection.899        /// 900        /// * item_id: ID of NFT to burn.901        #[weight = <T as Config>::WeightInfo::burn_item()]902        #[transactional]903        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {904905            let sender = ensure_signed(origin)?;906907            // Transfer permissions check908            let target_collection = Self::get_collection(collection_id)?;909            ensure!(910                Self::is_item_owner(sender.clone(), &target_collection, item_id) ||911                (912                    target_collection.limits.owner_can_transfer &&913                    Self::is_owner_or_admin_permissions(&target_collection, sender.clone())914                ),915                Error::<T>::NoPermission916            );917918            if target_collection.access == AccessMode::WhiteList {919                Self::check_white_list(&target_collection, &sender)?;920            }921922            match target_collection.mode923            {924                CollectionMode::NFT => Self::burn_nft_item(&target_collection, item_id)?,925                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, &target_collection, value)?,926                CollectionMode::ReFungible  => Self::burn_refungible_item(&target_collection, item_id, &sender)?,927                _ => ()928            };929930            // call event931            Self::deposit_event(RawEvent::ItemDestroyed(target_collection.id, item_id));932933            Ok(())934        }935936        /// Change ownership of the token.937        /// 938        /// # Permissions939        /// 940        /// * Collection Owner941        /// * Collection Admin942        /// * Current NFT owner943        ///944        /// # Arguments945        /// 946        /// * recipient: Address of token recipient.947        /// 948        /// * collection_id.949        /// 950        /// * item_id: ID of the item951        ///     * Non-Fungible Mode: Required.952        ///     * Fungible Mode: Ignored.953        ///     * Re-Fungible Mode: Required.954        /// 955        /// * value: Amount to transfer.956        ///     * Non-Fungible Mode: Ignored957        ///     * Fungible Mode: Must specify transferred amount958        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)959        #[weight = <T as Config>::WeightInfo::transfer()]960        #[transactional]961        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {962            let sender = ensure_signed(origin)?;963            let collection = Self::get_collection(collection_id)?;964965            Self::transfer_internal(sender, recipient, &collection, item_id, value)966        }967968        /// Set, change, or remove approved address to transfer the ownership of the NFT.969        /// 970        /// # Permissions971        /// 972        /// * Collection Owner973        /// * Collection Admin974        /// * Current NFT owner975        /// 976        /// # Arguments977        /// 978        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).979        /// 980        /// * collection_id.981        /// 982        /// * item_id: ID of the item.983        #[weight = <T as Config>::WeightInfo::approve()]984        #[transactional]985        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {986987            let sender = ensure_signed(origin)?;988            let target_collection = Self::get_collection(collection_id)?;989990            Self::token_exists(&target_collection, item_id)?;991992            // Transfer permissions check993            let bypasses_limits = target_collection.limits.owner_can_transfer &&994                Self::is_owner_or_admin_permissions(995                    &target_collection,996                    sender.clone(),997                );998999            let allowance_limit = if bypasses_limits {1000                None1001            } else if let Some(amount) = Self::owned_amount(1002                sender.clone(),1003                &target_collection,1004                item_id,1005            ) {1006                Some(amount)1007            } else {1008                fail!(Error::<T>::NoPermission);1009            };10101011            if target_collection.access == AccessMode::WhiteList {1012                Self::check_white_list(&target_collection, &sender)?;1013                Self::check_white_list(&target_collection, &spender)?;1014            }10151016            let allowance: u128 = amount1017                .checked_add(<Allowances<T>>::get(collection_id, (item_id, &sender, &spender)))1018                .ok_or(Error::<T>::NumOverflow)?;1019            if let Some(limit) = allowance_limit {1020                ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1021            }1022            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);10231024            Self::deposit_event(RawEvent::Approved(target_collection.id, item_id, sender, spender, allowance));1025            Ok(())1026        }1027        1028        /// 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.1029        /// 1030        /// # Permissions1031        /// * Collection Owner1032        /// * Collection Admin1033        /// * Current NFT owner1034        /// * Address approved by current NFT owner1035        /// 1036        /// # Arguments1037        /// 1038        /// * from: Address that owns token.1039        /// 1040        /// * recipient: Address of token recipient.1041        /// 1042        /// * collection_id.1043        /// 1044        /// * item_id: ID of the item.1045        /// 1046        /// * value: Amount to transfer.1047        #[weight = <T as Config>::WeightInfo::transfer_from()]1048        #[transactional]1049        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {10501051            let sender = ensure_signed(origin)?;1052            let target_collection = Self::get_collection(collection_id)?;10531054            // Check approval1055            let approval: u128 = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));10561057            // Limits check1058            Self::is_correct_transfer(&target_collection, &recipient)?;10591060            // Transfer permissions check         1061            ensure!(1062                approval >= value || 1063                (1064                    target_collection.limits.owner_can_transfer &&1065                    Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1066                ),1067                Error::<T>::NoPermission1068            );10691070            if target_collection.access == AccessMode::WhiteList {1071                Self::check_white_list(&target_collection, &sender)?;1072                Self::check_white_list(&target_collection, &recipient)?;1073            }10741075            // Reduce approval by transferred amount or remove if remaining approval drops to 01076            if approval.saturating_sub(value) > 0 {1077                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1078            }1079            else {1080                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1081            }10821083            match target_collection.mode1084            {1085                CollectionMode::NFT => Self::transfer_nft(&target_collection, item_id, from.clone(), recipient.clone())?,1086                CollectionMode::Fungible(_)  => Self::transfer_fungible(&target_collection, value, &from, &recipient)?,1087                CollectionMode::ReFungible  => Self::transfer_refungible(&target_collection, item_id, value, from.clone(), recipient.clone())?,1088                _ => ()1089            };10901091            Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, from, recipient, value));1092            Ok(())1093        }10941095        // #[weight = 0]1096        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {10971098        //     // let no_perm_mes = "You do not have permissions to modify this collection";1099        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1100        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1101        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11021103        //     // // on_nft_received  call11041105        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;11061107        //     Ok(())1108        // }11091110        /// Set off-chain data schema.1111        /// 1112        /// # Permissions1113        /// 1114        /// * Collection Owner1115        /// * Collection Admin1116        /// 1117        /// # Arguments1118        /// 1119        /// * collection_id.1120        /// 1121        /// * schema: String representing the offchain data schema.1122        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1123        #[transactional]1124        pub fn set_variable_meta_data (1125            origin,1126            collection_id: CollectionId,1127            item_id: TokenId,1128            data: Vec<u8>1129        ) -> DispatchResult {1130            let sender = ensure_signed(origin)?;1131            1132            let target_collection = Self::get_collection(collection_id)?;1133            Self::token_exists(&target_collection, item_id)?;11341135            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);11361137            // Modify permissions check1138            ensure!(Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1139                Self::is_owner_or_admin_permissions(&target_collection, sender.clone()),1140                Error::<T>::NoPermission);11411142            match target_collection.mode1143            {1144                CollectionMode::NFT => Self::set_nft_variable_data(&target_collection, item_id, data)?,1145                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(&target_collection, item_id, data)?,1146                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1147                _ => fail!(Error::<T>::UnexpectedCollectionType)1148            };11491150            Ok(())1151        }1152 1153        /// Set schema standard1154        /// ImageURL1155        /// Unique1156        /// 1157        /// # Permissions1158        /// 1159        /// * Collection Owner1160        /// * Collection Admin1161        /// 1162        /// # Arguments1163        /// 1164        /// * collection_id.1165        /// 1166        /// * schema: SchemaVersion: enum1167        #[weight = <T as Config>::WeightInfo::set_schema_version()]1168        #[transactional]1169        pub fn set_schema_version(1170            origin,1171            collection_id: CollectionId,1172            version: SchemaVersion1173        ) -> DispatchResult {1174            let sender = ensure_signed(origin)?;1175            let mut target_collection = Self::get_collection(collection_id)?;1176            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;1177            target_collection.schema_version = version;1178            Self::save_collection(target_collection);11791180            Ok(())1181        }11821183        /// Set off-chain data schema.1184        /// 1185        /// # Permissions1186        /// 1187        /// * Collection Owner1188        /// * Collection Admin1189        /// 1190        /// # Arguments1191        /// 1192        /// * collection_id.1193        /// 1194        /// * schema: String representing the offchain data schema.1195        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1196        #[transactional]1197        pub fn set_offchain_schema(1198            origin,1199            collection_id: CollectionId,1200            schema: Vec<u8>1201        ) -> DispatchResult {1202            let sender = ensure_signed(origin)?;1203            let mut target_collection = Self::get_collection(collection_id)?;1204            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;12051206            // check schema limit1207            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");12081209            target_collection.offchain_schema = schema;1210            Self::save_collection(target_collection);12111212            Ok(())1213        }12141215        /// Set const on-chain data schema.1216        /// 1217        /// # Permissions1218        /// 1219        /// * Collection Owner1220        /// * Collection Admin1221        /// 1222        /// # Arguments1223        /// 1224        /// * collection_id.1225        /// 1226        /// * schema: String representing the const on-chain data schema.1227        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1228        #[transactional]1229        pub fn set_const_on_chain_schema (1230            origin,1231            collection_id: CollectionId,1232            schema: Vec<u8>1233        ) -> DispatchResult {1234            let sender = ensure_signed(origin)?;1235            let mut target_collection = Self::get_collection(collection_id)?;1236            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;12371238            // check schema limit1239            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");12401241            target_collection.const_on_chain_schema = schema;1242            Self::save_collection(target_collection);12431244            Ok(())1245        }12461247        /// Set variable on-chain data schema.1248        /// 1249        /// # Permissions1250        /// 1251        /// * Collection Owner1252        /// * Collection Admin1253        /// 1254        /// # Arguments1255        /// 1256        /// * collection_id.1257        /// 1258        /// * schema: String representing the variable on-chain data schema.1259        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1260        #[transactional]1261        pub fn set_variable_on_chain_schema (1262            origin,1263            collection_id: CollectionId,1264            schema: Vec<u8>1265        ) -> DispatchResult {1266            let sender = ensure_signed(origin)?;1267            let mut target_collection = Self::get_collection(collection_id)?;1268            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;12691270            // check schema limit1271            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");12721273            target_collection.variable_on_chain_schema = schema;1274            Self::save_collection(target_collection);12751276            Ok(())1277        }12781279        // Sudo permissions function1280        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1281        #[transactional]1282        pub fn set_chain_limits(1283            origin,1284            limits: ChainLimits1285        ) -> DispatchResult {12861287            #[cfg(not(feature = "runtime-benchmarks"))]1288            ensure_root(origin)?;12891290            <ChainLimit>::put(limits);1291            Ok(())1292        }12931294        /// Enable smart contract self-sponsoring.1295        /// 1296        /// # Permissions1297        /// 1298        /// * Contract Owner1299        /// 1300        /// # Arguments1301        /// 1302        /// * contract address1303        /// * enable flag1304        /// 1305        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1306        #[transactional]1307        pub fn enable_contract_sponsoring(1308            origin,1309            contract_address: T::AccountId,1310            enable: bool1311        ) -> DispatchResult {13121313            let sender = ensure_signed(origin)?;13141315            #[cfg(feature = "runtime-benchmarks")]1316            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13171318            Self::ensure_contract_owned(sender, &contract_address)?;13191320            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1321            Ok(())1322        }13231324        /// Set the rate limit for contract sponsoring to specified number of blocks.1325        /// 1326        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1327        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1328        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1329        /// from contract endowment if there are at least B blocks between such transactions. 1330        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1331        /// 1332        /// # Permissions1333        /// 1334        /// * Contract Owner1335        /// 1336        /// # Arguments1337        /// 1338        /// -`contract_address`: Address of the contract to sponsor1339        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1340        /// 1341        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1342        #[transactional]1343        pub fn set_contract_sponsoring_rate_limit(1344            origin,1345            contract_address: T::AccountId,1346            rate_limit: T::BlockNumber1347        ) -> DispatchResult {1348            let sender = ensure_signed(origin)?;13491350            #[cfg(feature = "runtime-benchmarks")]1351            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13521353            Self::ensure_contract_owned(sender, &contract_address)?;1354            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1355            Ok(())1356        }13571358        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1359        /// 1360        /// # Permissions1361        /// 1362        /// * Address that deployed smart contract.1363        /// 1364        /// # Arguments1365        /// 1366        /// -`contract_address`: Address of the contract.1367        /// 1368        /// - `enable`: .  1369        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1370        #[transactional]1371        pub fn toggle_contract_white_list(1372            origin,1373            contract_address: T::AccountId,1374            enable: bool1375        ) -> DispatchResult {1376            let sender = ensure_signed(origin)?;13771378            #[cfg(feature = "runtime-benchmarks")]1379            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13801381            Self::ensure_contract_owned(sender, &contract_address)?;1382            if enable {1383                <ContractWhiteListEnabled<T>>::insert(contract_address, true);1384            } else {1385                <ContractWhiteListEnabled<T>>::remove(contract_address);1386            }1387            Ok(())1388        }1389        1390        /// Add an address to smart contract white list.1391        /// 1392        /// # Permissions1393        /// 1394        /// * Address that deployed smart contract.1395        /// 1396        /// # Arguments1397        /// 1398        /// -`contract_address`: Address of the contract.1399        ///1400        /// -`account_address`: Address to add.1401        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1402        #[transactional]1403        pub fn add_to_contract_white_list(1404            origin,1405            contract_address: T::AccountId,1406            account_address: T::AccountId1407        ) -> DispatchResult {1408            let sender = ensure_signed(origin)?;14091410            #[cfg(feature = "runtime-benchmarks")]1411            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1412            1413            Self::ensure_contract_owned(sender, &contract_address)?;      1414            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1415            Ok(())1416        }14171418        /// Remove an address from smart contract white list.1419        /// 1420        /// # Permissions1421        /// 1422        /// * Address that deployed smart contract.1423        /// 1424        /// # Arguments1425        /// 1426        /// -`contract_address`: Address of the contract.1427        ///1428        /// -`account_address`: Address to remove.1429        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1430        #[transactional]1431        pub fn remove_from_contract_white_list(1432            origin,1433            contract_address: T::AccountId,1434            account_address: T::AccountId1435        ) -> DispatchResult {1436            let sender = ensure_signed(origin)?;14371438            #[cfg(feature = "runtime-benchmarks")]1439            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14401441            Self::ensure_contract_owned(sender, &contract_address)?;1442            <ContractWhiteList<T>>::remove(contract_address, account_address);1443            Ok(())1444        }14451446        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1447        #[transactional]1448        pub fn set_collection_limits(1449            origin,1450            collection_id: u32,1451            new_limits: CollectionLimits<T::BlockNumber>,1452        ) -> DispatchResult {1453            let sender = ensure_signed(origin)?;1454            let mut target_collection = Self::get_collection(collection_id)?;1455            Self::check_owner_permissions(&target_collection, sender.clone())?;1456            let old_limits = &target_collection.limits;1457            let chain_limits = ChainLimit::get();14581459            // collection bounds1460            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1461                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1462                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1463                Error::<T>::CollectionLimitBoundsExceeded);14641465            // token_limit   check  prev1466            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1467            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);14681469            ensure!(1470                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1471                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1472                Error::<T>::OwnerPermissionsCantBeReverted,1473            );14741475            target_collection.limits = new_limits;1476            Self::save_collection(target_collection);14771478            Ok(())1479        } 1480    }1481}14821483impl<T: Config> Module<T> {1484    pub fn create_item_internal(sender: T::AccountId, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1485        let target_collection = Self::get_collection(collection_id)?;14861487        Self::can_create_items_in_collection(&target_collection, &sender, &owner, 1)?;1488        Self::validate_create_item_args(&target_collection, &data)?;1489        Self::create_item_no_validation(&target_collection, owner, data)?;14901491        Ok(())1492    }14931494    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1495        // Limits check1496        Self::is_correct_transfer(target_collection, &recipient)?;14971498        // Transfer permissions check1499        ensure!(Self::is_item_owner(sender.clone(), target_collection, item_id) ||1500            Self::is_owner_or_admin_permissions(target_collection, sender.clone()),1501            Error::<T>::NoPermission);15021503        if target_collection.access == AccessMode::WhiteList {1504            Self::check_white_list(target_collection, &sender)?;1505            Self::check_white_list(target_collection, &recipient)?;1506        }15071508        match target_collection.mode1509        {1510            CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1511            CollectionMode::Fungible(_)  => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1512            CollectionMode::ReFungible  => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1513            _ => ()1514        };15151516        Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender, recipient, value));15171518        Ok(())1519    }152015211522    fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::AccountId) -> DispatchResult {1523        let collection_id = collection.id;15241525        // check token limit and account token limit1526        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1527        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1528        1529        Ok(())1530    }15311532    fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::AccountId, owner: &T::AccountId, amount: u32) -> DispatchResult {1533        let collection_id = collection.id;15341535        // check token limit and account token limit1536        let total_items: u32 = ItemListIndex::get(collection_id)1537            .checked_add(amount)1538            .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1539        let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner).len() as u32)1540            .checked_add(amount)1541            .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1542        ensure!(collection.limits.token_limit >= total_items,  Error::<T>::CollectionTokenLimitExceeded);1543        ensure!(collection.limits.account_token_ownership_limit >= account_items,  Error::<T>::AccountTokenLimitExceeded);15441545        if !Self::is_owner_or_admin_permissions(collection, sender.clone()) {1546            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1547            Self::check_white_list(collection, owner)?;1548            Self::check_white_list(collection, sender)?;1549        }15501551        Ok(())1552    }15531554    fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1555        match target_collection.mode1556        {1557            CollectionMode::NFT => {1558                if let CreateItemData::NFT(data) = data {1559                    // check sizes1560                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1561                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1562                } else {1563                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1564                }1565            },1566            CollectionMode::Fungible(_) => {1567                if let CreateItemData::Fungible(_) = data {1568                } else {1569                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1570                }1571            },1572            CollectionMode::ReFungible => {1573                if let CreateItemData::ReFungible(data) = data {15741575                    // check sizes1576                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1577                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);15781579                    // Check refungibility limits1580                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1581                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1582                } else {1583                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1584                }1585            },1586            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1587        };15881589        Ok(())1590    }15911592    fn create_item_no_validation(collection: &CollectionHandle<T>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1593        match data1594        {1595            CreateItemData::NFT(data) => {1596                let item = NftItemType {1597                    owner: owner.clone(),1598                    const_data: data.const_data,1599                    variable_data: data.variable_data1600                };16011602                Self::add_nft_item(collection, item)?;1603            },1604            CreateItemData::Fungible(data) => {1605                Self::add_fungible_item(collection, &owner, data.value)?;1606            },1607            CreateItemData::ReFungible(data) => {1608                let mut owner_list = Vec::new();1609                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});16101611                let item = ReFungibleItemType {1612                    owner: owner_list,1613                    const_data: data.const_data,1614                    variable_data: data.variable_data1615                };16161617                Self::add_refungible_item(collection, item)?;1618            }1619        };16201621        Ok(())1622    }16231624    fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::AccountId, value: u128) -> DispatchResult {1625        let collection_id = collection.id;16261627        // Does new owner already have an account?1628        let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner).value;16291630        // Mint 1631        let item = FungibleItemType {1632            value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1633        };1634        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16351636        // Update balance1637        let new_balance = <Balance<T>>::get(collection_id, owner)1638            .checked_add(value)1639            .ok_or(Error::<T>::NumOverflow)?;1640        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);16411642        Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1643        Ok(())1644    }16451646    fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1647        let collection_id = collection.id;16481649        let current_index = <ItemListIndex>::get(collection_id)1650            .checked_add(1)1651            .ok_or(Error::<T>::NumOverflow)?;1652        let itemcopy = item.clone();16531654        ensure!(1655            item.owner.len() == 1,1656            Error::<T>::BadCreateRefungibleCall,1657        );1658        let item_owner = item.owner.first().expect("only one owner is defined");16591660        let value = item_owner.fraction;1661        let owner = item_owner.owner.clone();16621663        Self::add_token_index(collection_id, current_index, &owner)?;16641665        <ItemListIndex>::insert(collection_id, current_index);1666        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);16671668        // Update balance1669        let new_balance = <Balance<T>>::get(collection_id, &owner)1670            .checked_add(value)1671            .ok_or(Error::<T>::NumOverflow)?;1672        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);16731674        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1675        Ok(())1676    }16771678    fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::AccountId>) -> DispatchResult {1679        let collection_id = collection.id;16801681        let current_index = <ItemListIndex>::get(collection_id)1682            .checked_add(1)1683            .ok_or(Error::<T>::NumOverflow)?;16841685        let item_owner = item.owner.clone();1686        Self::add_token_index(collection_id, current_index, &item.owner)?;16871688        <ItemListIndex>::insert(collection_id, current_index);1689        <NftItemList<T>>::insert(collection_id, current_index, item);16901691        // Update balance1692        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1693            .checked_add(1)1694            .ok_or(Error::<T>::NumOverflow)?;1695        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);16961697        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));1698        Ok(())1699    }17001701    fn burn_refungible_item(1702        collection: &CollectionHandle<T>,1703        item_id: TokenId,1704        owner: &T::AccountId,1705    ) -> DispatchResult {1706        let collection_id = collection.id;17071708        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1709            .ok_or(Error::<T>::TokenNotFound)?;1710        let rft_balance = token1711            .owner1712            .iter()1713            .find(|&i| i.owner == *owner)1714            .ok_or(Error::<T>::TokenNotFound)?;1715        Self::remove_token_index(collection_id, item_id, owner)?;17161717        // update balance1718        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1719            .checked_sub(rft_balance.fraction)1720            .ok_or(Error::<T>::NumOverflow)?;1721        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);17221723        // Re-create owners list with sender removed1724        let index = token1725            .owner1726            .iter()1727            .position(|i| i.owner == *owner)1728            .expect("owned item is exists");1729        token.owner.remove(index);1730        let owner_count = token.owner.len();17311732        // Burn the token completely if this was the last (only) owner1733        if owner_count == 0 {1734            <ReFungibleItemList<T>>::remove(collection_id, item_id);1735            <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1736        }1737        else {1738            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1739        }17401741        Ok(())1742    }17431744    fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1745        let collection_id = collection.id;17461747        let item = <NftItemList<T>>::get(collection_id, item_id)1748            .ok_or(Error::<T>::TokenNotFound)?;1749        Self::remove_token_index(collection_id, item_id, &item.owner)?;17501751        // update balance1752        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1753            .checked_sub(1)1754            .ok_or(Error::<T>::NumOverflow)?;1755        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1756        <NftItemList<T>>::remove(collection_id, item_id);1757        <VariableMetaDataBasket<T>>::remove(collection_id, item_id);17581759        Ok(())1760    }17611762    fn burn_fungible_item(owner: &T::AccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {1763        let collection_id = collection.id;17641765        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1766        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);17671768        // update balance1769        let new_balance = <Balance<T>>::get(collection_id, owner)1770            .checked_sub(value)1771            .ok_or(Error::<T>::NumOverflow)?;1772        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17731774        if balance.value - value > 0 {1775            balance.value -= value;1776            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1777        }1778        else {1779            <FungibleItemList<T>>::remove(collection_id, owner);1780        }17811782        Ok(())1783    }17841785    pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1786        Ok(<CollectionById<T>>::get(collection_id)1787            .map(|collection| CollectionHandle {1788                id: collection_id,1789                collection1790            })1791            .ok_or(Error::<T>::CollectionNotFound)?)1792    }17931794    fn save_collection(collection: CollectionHandle<T>) {1795        <CollectionById<T>>::insert(collection.id, collection.collection);1796    }17971798    fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: T::AccountId) -> DispatchResult {1799        ensure!(1800            subject == target_collection.owner,1801            Error::<T>::NoPermission1802        );18031804        Ok(())1805    }18061807    fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: T::AccountId) -> bool {1808        subject == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)1809    }18101811    fn check_owner_or_admin_permissions(1812        collection: &CollectionHandle<T>,1813        subject: T::AccountId,1814    ) -> DispatchResult {1815        ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);18161817        Ok(())1818    }18191820    fn owned_amount(1821        subject: T::AccountId,1822        target_collection: &CollectionHandle<T>,1823        item_id: TokenId,1824    ) -> Option<u128> {1825        let collection_id = target_collection.id;18261827        match target_collection.mode {1828            CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == subject)1829                .then(|| 1),1830            CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject)1831                .value),1832            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1833                .owner1834                .iter()1835                .find(|i| i.owner == subject)1836                .map(|i| i.fraction),1837            CollectionMode::Invalid => None,1838        }1839    }18401841    fn is_item_owner(subject: T::AccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {1842        match target_collection.mode {1843            CollectionMode::Fungible(_) => true,1844            _ => Self::owned_amount(subject, target_collection, item_id).is_some(),1845        }1846    }18471848    fn check_white_list(collection: &CollectionHandle<T>, address: &T::AccountId) -> DispatchResult {1849        let collection_id = collection.id;18501851        let mes = Error::<T>::AddresNotInWhiteList;1852        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);18531854        Ok(())1855    }18561857    /// Check if token exists. In case of Fungible, check if there is an entry for 1858    /// the owner in fungible balances double map1859    fn token_exists(1860        target_collection: &CollectionHandle<T>,1861        item_id: TokenId,1862    ) -> DispatchResult {1863        let collection_id = target_collection.id;1864        let exists = match target_collection.mode1865        {1866            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1867            CollectionMode::Fungible(_)  => true,1868            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1869            _ => false1870        };18711872        ensure!(exists == true, Error::<T>::TokenNotFound);1873        Ok(())1874    }18751876    fn transfer_fungible(1877        collection: &CollectionHandle<T>,1878        value: u128,1879        owner: &T::AccountId,1880        recipient: &T::AccountId,1881    ) -> DispatchResult {1882        let collection_id = collection.id;18831884        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1885        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);18861887        // Send balance to recipient (updates balanceOf of recipient)1888        Self::add_fungible_item(collection, recipient, value)?;18891890        // update balanceOf of sender1891        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);18921893        // Reduce or remove sender1894        if balance.value == value {1895            <FungibleItemList<T>>::remove(collection_id, owner);1896        }1897        else {1898            balance.value -= value;1899            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1900        }19011902        Ok(())1903    }19041905    fn transfer_refungible(1906        collection: &CollectionHandle<T>,1907        item_id: TokenId,1908        value: u128,1909        owner: T::AccountId,1910        new_owner: T::AccountId,1911    ) -> DispatchResult {1912        let collection_id = collection.id;1913        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)1914            .ok_or(Error::<T>::TokenNotFound)?;19151916        let item = full_item1917            .owner1918            .iter()1919            .filter(|i| i.owner == owner)1920            .next()1921            .ok_or(Error::<T>::TokenNotFound)?;1922        let amount = item.fraction;19231924        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19251926        // update balance1927        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1928            .checked_sub(value)1929            .ok_or(Error::<T>::NumOverflow)?;1930        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19311932        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1933            .checked_add(value)1934            .ok_or(Error::<T>::NumOverflow)?;1935        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19361937        let old_owner = item.owner.clone();1938        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19391940        // transfer1941        if amount == value && !new_owner_has_account {1942            // change owner1943            // new owner do not have account1944            let mut new_full_item = full_item.clone();1945            new_full_item1946                .owner1947                .iter_mut()1948                .find(|i| i.owner == owner)1949                .expect("old owner does present in refungible")1950                .owner = new_owner.clone();1951            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19521953            // update index collection1954            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;1955        } else {1956            let mut new_full_item = full_item.clone();1957            new_full_item1958                .owner1959                .iter_mut()1960                .find(|i| i.owner == owner)1961                .expect("old owner does present in refungible")1962                .fraction -= value;19631964            // separate amount1965            if new_owner_has_account {1966                // new owner has account1967                new_full_item1968                    .owner1969                    .iter_mut()1970                    .find(|i| i.owner == new_owner)1971                    .expect("new owner has account")1972                    .fraction += value;1973            } else {1974                // new owner do not have account1975                new_full_item.owner.push(Ownership {1976                    owner: new_owner.clone(),1977                    fraction: value,1978                });1979                Self::add_token_index(collection_id, item_id, &new_owner)?;1980            }19811982            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1983        }19841985        Ok(())1986    }19871988    fn transfer_nft(1989        collection: &CollectionHandle<T>,1990        item_id: TokenId,1991        sender: T::AccountId,1992        new_owner: T::AccountId,1993    ) -> DispatchResult {1994        let collection_id = collection.id;1995        let mut item = <NftItemList<T>>::get(collection_id, item_id)1996            .ok_or(Error::<T>::TokenNotFound)?;19971998        ensure!(1999            sender == item.owner,2000            Error::<T>::MustBeTokenOwner2001        );20022003        // update balance2004        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2005            .checked_sub(1)2006            .ok_or(Error::<T>::NumOverflow)?;2007        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20082009        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2010            .checked_add(1)2011            .ok_or(Error::<T>::NumOverflow)?;2012        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20132014        // change owner2015        let old_owner = item.owner.clone();2016        item.owner = new_owner.clone();2017        <NftItemList<T>>::insert(collection_id, item_id, item);20182019        // update index collection2020        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;20212022        Ok(())2023    }2024    2025    fn set_re_fungible_variable_data(2026        collection: &CollectionHandle<T>,2027        item_id: TokenId,2028        data: Vec<u8>2029    ) -> DispatchResult {2030        let collection_id = collection.id;2031        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2032            .ok_or(Error::<T>::TokenNotFound)?;20332034        item.variable_data = data;20352036        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20372038        Ok(())2039    }20402041    fn set_nft_variable_data(2042        collection: &CollectionHandle<T>,2043        item_id: TokenId,2044        data: Vec<u8>2045    ) -> DispatchResult {2046        let collection_id = collection.id;2047        let mut item = <NftItemList<T>>::get(collection_id, item_id)2048            .ok_or(Error::<T>::TokenNotFound)?;2049        2050        item.variable_data = data;20512052        <NftItemList<T>>::insert(collection_id, item_id, item);2053        2054        Ok(())2055    }20562057    fn init_collection(item: &Collection<T>) {2058        // check params2059        assert!(2060            item.decimal_points <= MAX_DECIMAL_POINTS,2061            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2062        );2063        assert!(2064            item.name.len() <= 64,2065            "Collection name can not be longer than 63 char"2066        );2067        assert!(2068            item.name.len() <= 256,2069            "Collection description can not be longer than 255 char"2070        );2071        assert!(2072            item.token_prefix.len() <= 16,2073            "Token prefix can not be longer than 15 char"2074        );20752076        // Generate next collection ID2077        let next_id = CreatedCollectionCount::get()2078            .checked_add(1)2079            .unwrap();20802081        CreatedCollectionCount::put(next_id);2082    }20832084    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2085        let current_index = <ItemListIndex>::get(collection_id)2086            .checked_add(1)2087            .unwrap();20882089        let item_owner = item.owner.clone();2090        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();20912092        <ItemListIndex>::insert(collection_id, current_index);20932094        // Update balance2095        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2096            .checked_add(1)2097            .unwrap();2098        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2099    }21002101    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2102        let current_index = <ItemListIndex>::get(collection_id)2103            .checked_add(1)2104            .unwrap();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(item.value)2113            .unwrap();2114        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2115    }21162117    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2118        let current_index = <ItemListIndex>::get(collection_id)2119            .checked_add(1)2120            .unwrap();21212122        let value = item.owner.first().unwrap().fraction;2123        let owner = item.owner.first().unwrap().owner.clone();21242125        Self::add_token_index(collection_id, current_index, &owner).unwrap();21262127        <ItemListIndex>::insert(collection_id, current_index);21282129        // Update balance2130        let new_balance = <Balance<T>>::get(collection_id, &owner)2131            .checked_add(value)2132            .unwrap();2133        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2134    }21352136    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {2137        // add to account limit2138        if <AccountItemCount<T>>::contains_key(owner) {21392140            // bound Owned tokens by a single address2141            let count = <AccountItemCount<T>>::get(owner);2142            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21432144            <AccountItemCount<T>>::insert(owner.clone(), count2145                .checked_add(1)2146                .ok_or(Error::<T>::NumOverflow)?);2147        }2148        else {2149            <AccountItemCount<T>>::insert(owner.clone(), 1);2150        }21512152        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2153        if list_exists {2154            let mut list = <AddressTokens<T>>::get(collection_id, owner);2155            let item_contains = list.contains(&item_index.clone());21562157            if !item_contains {2158                list.push(item_index.clone());2159            }21602161            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2162        } else {2163            let mut itm = Vec::new();2164            itm.push(item_index.clone());2165            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2166        }21672168        Ok(())2169    }21702171    fn remove_token_index(2172        collection_id: CollectionId,2173        item_index: TokenId,2174        owner: &T::AccountId,2175    ) -> DispatchResult {21762177        // update counter2178        <AccountItemCount<T>>::insert(owner.clone(), 2179            <AccountItemCount<T>>::get(owner)2180            .checked_sub(1)2181            .ok_or(Error::<T>::NumOverflow)?);218221832184        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2185        if list_exists {2186            let mut list = <AddressTokens<T>>::get(collection_id, owner);2187            let item_contains = list.contains(&item_index.clone());21882189            if item_contains {2190                list.retain(|&item| item != item_index);2191                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2192            }2193        }21942195        Ok(())2196    }21972198    fn move_token_index(2199        collection_id: CollectionId,2200        item_index: TokenId,2201        old_owner: &T::AccountId,2202        new_owner: &T::AccountId,2203    ) -> DispatchResult {2204        Self::remove_token_index(collection_id, item_index, old_owner)?;2205        Self::add_token_index(collection_id, item_index, new_owner)?;22062207        Ok(())2208    }2209    2210    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2211        ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);22122213        Ok(())2214    }2215}
modifiedpallets/scheduler/src/lib.rsdiffbeforeafterboth
--- a/pallets/scheduler/src/lib.rs
+++ b/pallets/scheduler/src/lib.rs
@@ -102,7 +102,7 @@
 	/// Not strictly enforced, but used for weight estimation.
 	type MaxScheduledPerBlock: Get<u32>;
 
-	/// TODO
+	/// Sponsoring function
 	type Sponsoring: SponsoringResolve<Self::AccountId, <Self as Config>::Call>;
 
 	/// Weight information for extrinsics in this pallet.
@@ -230,8 +230,7 @@
 		///     - 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]
+		#[weight = <T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get())]
 		fn schedule(origin,
 			when: T::BlockNumber,
 			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,
@@ -367,7 +366,7 @@
 			}
 			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());
+			let mut total_weight: Weight = 0;
 			queued.into_iter()
 				.enumerate()
 				.scan(base_weight, |cumulative_weight, (order, (index, s))| {
@@ -407,7 +406,7 @@
 						).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());
+							sender);
 						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();
modifiedtests/src/pallet-presence.test.tsdiffbeforeafterboth
--- a/tests/src/pallet-presence.test.ts
+++ b/tests/src/pallet-presence.test.ts
@@ -13,7 +13,8 @@
 
 // Pallets that must always be present
 const requiredPallets = [
-  'nft', 'inflation', 'balances', 'contracts', 'randomnesscollectiveflip', 'system', 'timestamp', 'transactionpayment', 'treasury', 'vesting'
+  'nft', 'inflation', 'balances', 'contracts', 'randomnesscollectiveflip', 'system', 'timestamp', 'transactionpayment', 'treasury', 'vesting',
+  'scheduler', 'nftpayment', 'charging'
 ];
 
 // Pallets that depend on consensus and governance configuration
addedtests/src/scheduler.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/scheduler.test.ts
@@ -0,0 +1,94 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+
+import { IKeyringPair } from '@polkadot/types/types';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import privateKey from './substrate/privateKey';
+import usingApi from './substrate/substrate-api';
+import {
+  createItemExpectSuccess,
+  createCollectionExpectSuccess,
+  destroyCollectionExpectSuccess,
+  findNotExistingCollection,
+  queryCollectionExpectSuccess,
+  setOffchainSchemaExpectFailure,
+  setOffchainSchemaExpectSuccess,
+  transferExpectSuccess,
+  scheduleTransferExpectSuccess,
+  setCollectionSponsorExpectSuccess,
+  confirmSponsorshipExpectSuccess
+} from './util/helpers';
+
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+const DATA = [1, 2, 3, 4];
+
+describe('Integration Test scheduler base transaction', () => {
+  let alice: IKeyringPair;
+
+  before(async () => {
+    await usingApi(async () => {
+      alice = privateKey('//Alice');
+    });
+  });
+
+  it('User can transfer owned token with delay (scheduler)', async () => {
+    await usingApi(async (api) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      await setCollectionSponsorExpectSuccess(nftCollectionId, Alice.address);
+      await confirmSponsorshipExpectSuccess(nftCollectionId);
+
+      await scheduleTransferExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob, 1, 'NFT');
+    });
+  });
+
+  
+});
+
+// describe('Negative Integration Test setOffchainSchema', () => {
+//   let alice: IKeyringPair;
+//   let bob: IKeyringPair;
+
+//   let validCollectionId: number;
+
+//   before(async () => {
+//     await usingApi(async () => {
+//       alice = privateKey('//Alice');
+//       bob = privateKey('//Bob');
+
+//       validCollectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } });
+//     });
+//   });
+
+//   it('fails on not existing collection id', async () => {
+//     const nonExistingCollectionId = await usingApi(findNotExistingCollection);
+
+//     await setOffchainSchemaExpectFailure(alice, nonExistingCollectionId, DATA);
+//   });
+
+//   it('fails on destroyed collection id', async () => {
+//     const destroyedCollectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } });
+//     await destroyCollectionExpectSuccess(destroyedCollectionId);
+
+//     await setOffchainSchemaExpectFailure(alice, destroyedCollectionId, DATA);
+//   });
+
+//   it('fails on too long data', async () => {
+//     const tooLongData = new Array(4097).fill(0xff);
+
+//     await setOffchainSchemaExpectFailure(alice, validCollectionId, tooLongData);
+//   });
+
+//   it('fails on execution by non-owner', async () => {
+//     await setOffchainSchemaExpectFailure(bob, validCollectionId, DATA);
+//   });
+// });
modifiedtests/src/util/helpers.tsdiffbeforeafterboth
--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -5,7 +5,7 @@
 
 import { ApiPromise, Keyring } from '@polkadot/api';
 import { Enum, Struct } from '@polkadot/types/codec';
-import type { AccountId, EventRecord } from '@polkadot/types/interfaces';
+import type { AccountId, BlockNumber, Call, EventRecord } from '@polkadot/types/interfaces';
 import { u128 } from '@polkadot/types/primitive';
 import { IKeyringPair } from '@polkadot/types/types';
 import { BigNumber } from 'bignumber.js';
@@ -17,6 +17,8 @@
 import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from '../substrate/substrate-api';
 import { ICollectionInterface } from '../types';
 import { hexToStr, strToUTF16, utf16ToStr } from './util';
+import { Compact, Option, Raw, Vec } from '@polkadot/types/codec';
+// import { AccountId, AccountIdOf, AccountIndex, Address, AssetId, Balance, BalanceOf, Block, BlockNumber, Call, ChangesTrieConfiguration, Consensus, ConsensusEngineId, Digest, DigestItem, DispatchClass, DispatchInfo, DispatchInfoTo190, EcdsaSignature, Ed25519Signature, Extrinsic, ExtrinsicEra, ExtrinsicPayload, ExtrinsicPayloadUnknown, ExtrinsicPayloadV1, ExtrinsicPayloadV2, ExtrinsicPayloadV3, ExtrinsicPayloadV4, ExtrinsicSignatureV1, ExtrinsicSignatureV2, ExtrinsicSignatureV3, ExtrinsicSignatureV4, ExtrinsicUnknown, ExtrinsicV1, ExtrinsicV2, ExtrinsicV3, ExtrinsicV4, Hash, Header, ImmortalEra, Index, Justification, KeyTypeId, KeyValue, LockIdentifier, LookupSource, LookupTarget, Moment, MortalEra, MultiSignature, Origin, Perbill, Percent, Permill, Perquintill, Phantom, PhantomData, PreRuntime, Seal, SealV0, Signature, SignedBlock, SignerPayload, Sr25519Signature, ValidatorId, Weight, WeightMultiplier } from '@polkadot/types/interfaces/runtime';
 
 chai.use(chaiAsPromised);
 const expect = chai.expect;
@@ -702,6 +704,54 @@
   });
 }
 
+async function getBlockNumber(api: ApiPromise): Promise<number> {
+  return new Promise<number>(async (resolve, reject) => {
+    const unsubscribe = await api.rpc.chain.subscribeNewHeads((head) => {
+        unsubscribe();
+        resolve(head.number.toNumber());
+    });
+  });
+}
+
+export async function
+scheduleTransferExpectSuccess(collectionId: number,
+                      tokenId: number,
+                      sender: IKeyringPair,
+                      recipient: IKeyringPair,
+                      value: number | bigint = 1,
+                      type: string = 'NFT') {
+  await usingApi(async (api: ApiPromise) => {
+    let balanceBefore = new BN(0);
+
+
+    let blockNumber: number | undefined = await getBlockNumber(api);
+    let expectedBlockNumber = blockNumber + 2;
+
+    expect(blockNumber).to.be.greaterThan(0);
+    const transferTx = await api.tx.nft.transfer(recipient.address, collectionId, tokenId, value); 
+    const scheduleTx = await api.tx.scheduler.schedule(expectedBlockNumber, null, 0, transferTx);
+
+    const events = await submitTransactionAsync(sender, scheduleTx);
+
+    const sponsorBalanceBefore = new BigNumber((await api.query.system.account(sender.address)).data.free.toString());
+    const recipientBalanceBefore = new BigNumber((await api.query.system.account(recipient.address)).data.free.toString());
+
+    const nftItemDataBefore = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON() as unknown as ITokenDataType;
+    expect(nftItemDataBefore.Owner.toString()).to.be.equal(sender.address);
+
+    // sleep for 2 blocks
+    await new Promise(resolve => setTimeout(resolve, 6000 * 2));
+
+    const sponsorBalanceAfter = new BigNumber((await api.query.system.account(sender.address)).data.free.toString());
+    const recipientBalanceAfter = new BigNumber((await api.query.system.account(recipient.address)).data.free.toString());
+
+    const nftItemData = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON() as unknown as ITokenDataType;
+    expect(nftItemData.Owner.toString()).to.be.equal(recipient.address);
+    expect(recipientBalanceAfter.toNumber()).to.be.equal(recipientBalanceBefore.toNumber());
+  });
+}
+
+
 export async function
 transferExpectSuccess(collectionId: number,
                       tokenId: number,