git.delta.rocks / unique-network / refs/commits / 9ecb329aa0c0

difftreelog

Merge remote-tracking branch 'origin/develop' into feature/NFTPAR-241

kpozdnikin2021-01-18parents: #f4eeb2e #07025d8.patch.diff
in: master
# Conflicts:
#	tests/package.json

16 files changed

modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
before · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, WithdrawReason,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial,29    },30    IsSubType, StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use pallet_contracts::ContractAddressFor;45use sp_runtime::traits::StaticLookup;4647#[cfg(test)]48mod mock;4950#[cfg(test)]51mod tests;5253mod default_weights;5455pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;56pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;57pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;5859// Structs60// #region6162pub type CollectionId = u32;63pub type TokenId = u32;64pub type DecimalPoints = u8;6566#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]67#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]68pub enum CollectionMode {69    Invalid,70    NFT,71    // decimal points72    Fungible(DecimalPoints),73    // decimal points74    ReFungible(DecimalPoints),75}7677impl Into<u8> for CollectionMode {78    fn into(self) -> u8 {79        match self {80            CollectionMode::Invalid => 0,81            CollectionMode::NFT => 1,82            CollectionMode::Fungible(_) => 2,83            CollectionMode::ReFungible(_) => 3,84        }85    }86}8788#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]89#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]90pub enum AccessMode {91    Normal,92    WhiteList,93}94impl Default for AccessMode {95    fn default() -> Self {96        Self::Normal97    }98}99100impl Default for CollectionMode {101    fn default() -> Self {102        Self::Invalid103    }104}105106#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]107#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]108pub enum SchemaVersion {109    ImageURL,110    Unique,111}112impl Default for SchemaVersion {113    fn default() -> Self {114        Self::ImageURL115    }116}117118#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]119#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]120pub struct Ownership<AccountId> {121    pub owner: AccountId,122    pub fraction: u128,123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct CollectionType<AccountId> {128    pub owner: AccountId,129    pub mode: CollectionMode,130    pub access: AccessMode,131    pub decimal_points: DecimalPoints,132    pub name: Vec<u16>,        // 64 include null escape char133    pub description: Vec<u16>, // 256 include null escape char134    pub token_prefix: Vec<u8>, // 16 include null escape char135    pub mint_mode: bool,136    pub offchain_schema: Vec<u8>,137    pub schema_version: SchemaVersion,138    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender139    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.140    pub limits: CollectionLimits, // Collection private restrictions 141    pub variable_on_chain_schema: Vec<u8>, //142    pub const_on_chain_schema: Vec<u8>, //143}144145#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]146#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]147pub struct NftItemType<AccountId> {148    pub owner: AccountId,149    pub const_data: Vec<u8>,150    pub variable_data: Vec<u8>,151}152153#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]154#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]155pub struct FungibleItemType {156    pub value: u128,157}158159#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]160#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]161pub struct ReFungibleItemType<AccountId> {162    pub owner: Vec<Ownership<AccountId>>,163    pub const_data: Vec<u8>,164    pub variable_data: Vec<u8>,165}166167// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]168// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169// pub struct VestingItem<AccountId, Moment> {170//     pub sender: AccountId,171//     pub recipient: AccountId,172//     pub collection_id: CollectionId,173//     pub item_id: TokenId,174//     pub amount: u64,175//     pub vesting_date: Moment,176// }177178#[derive(Encode, Decode, Debug, Clone, PartialEq)]179#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]180pub struct CollectionLimits {181    pub account_token_ownership_limit: u32,182    pub sponsored_data_size: u32,183    pub token_limit: u32,184185    // Timeouts for item types in passed blocks186    pub sponsor_transfer_timeout: u32,187}188189impl Default for CollectionLimits {190    fn default() -> CollectionLimits {191        CollectionLimits { 192            account_token_ownership_limit: 10_000_000, 193            token_limit: u32::max_value(),194            sponsored_data_size: u32::max_value(), 195            sponsor_transfer_timeout: 14400 }196    }197}198199#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]200#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]201pub struct ChainLimits {202    pub collection_numbers_limit: u32,203    pub account_token_ownership_limit: u32,204    pub collections_admins_limit: u64,205    pub custom_data_limit: u32,206207    // Timeouts for item types in passed blocks208    pub nft_sponsor_transfer_timeout: u32,209    pub fungible_sponsor_transfer_timeout: u32,210    pub refungible_sponsor_transfer_timeout: u32,211}212213pub trait WeightInfo {214	fn create_collection() -> Weight;215	fn destroy_collection() -> Weight;216	fn add_to_white_list() -> Weight;217	fn remove_from_white_list() -> Weight;218    fn set_public_access_mode() -> Weight;219    fn set_mint_permission() -> Weight;220    fn change_collection_owner() -> Weight;221    fn add_collection_admin() -> Weight;222    fn remove_collection_admin() -> Weight;223    fn set_collection_sponsor() -> Weight;224    fn confirm_sponsorship() -> Weight;225    fn remove_collection_sponsor() -> Weight;226    fn create_item(s: usize) -> Weight;227    fn burn_item() -> Weight;228    fn transfer() -> Weight;229    fn approve() -> Weight;230    fn transfer_from() -> Weight;231    fn set_offchain_schema() -> Weight;232    fn set_const_on_chain_schema() -> Weight;233    fn set_variable_on_chain_schema() -> Weight;234    fn set_variable_meta_data() -> Weight;235    fn enable_contract_sponsoring() -> Weight;236    fn set_schema_version() -> Weight;237    fn set_chain_limits() -> Weight;238    fn set_contract_sponsoring_rate_limit() -> Weight;239    fn toggle_contract_white_list() -> Weight;240    fn add_to_contract_white_list() -> Weight;241    fn remove_from_contract_white_list() -> Weight;242    fn set_collection_limits() -> Weight;243}244245#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]246#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]247pub struct CreateNftData {248    pub const_data: Vec<u8>,249    pub variable_data: Vec<u8>,250}251252#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]253#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]254pub struct CreateFungibleData {255    pub value: u128,256}257258#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]259#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]260pub struct CreateReFungibleData {261    pub const_data: Vec<u8>,262    pub variable_data: Vec<u8>,263}264265#[derive(Encode, Decode, Debug, Clone, PartialEq)]266#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]267pub enum CreateItemData {268    NFT(CreateNftData),269    Fungible(CreateFungibleData),270    ReFungible(CreateReFungibleData),271}272273impl CreateItemData {274    pub fn len(&self) -> usize {275        let len = match self {276            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),277            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),278            _ => 0279        };280        281        return len;282    }283}284285impl From<CreateNftData> for CreateItemData {286    fn from(item: CreateNftData) -> Self {287        CreateItemData::NFT(item)288    }289}290291impl From<CreateReFungibleData> for CreateItemData {292    fn from(item: CreateReFungibleData) -> Self {293        CreateItemData::ReFungible(item)294    }295}296297impl From<CreateFungibleData> for CreateItemData {298    fn from(item: CreateFungibleData) -> Self {299        CreateItemData::Fungible(item)300    }301}302303304decl_error! {305	/// Error for non-fungible-token module.306	pub enum Error for Module<T: Trait> {307        /// Total collections bound exceeded.308        TotalCollectionsLimitExceeded,309		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.310        CollectionDecimalPointLimitExceeded, 311        /// Collection name can not be longer than 63 char.312        CollectionNameLimitExceeded, 313        /// Collection description can not be longer than 255 char.314        CollectionDescriptionLimitExceeded, 315        /// Token prefix can not be longer than 15 char.316        CollectionTokenPrefixLimitExceeded,317        /// This collection does not exist.318        CollectionNotFound,319        /// Item not exists.320        TokenNotFound,321        /// Arithmetic calculation overflow.322        NumOverflow,       323        /// Account already has admin role.324        AlreadyAdmin,  325        /// You do not own this collection.326        NoPermission,327        /// This address is not set as sponsor, use setCollectionSponsor first.328        ConfirmUnsetSponsorFail,329        /// Collection is not in mint mode.330        PublicMintingNotAllowed,331        /// Sender parameter and item owner must be equal.332        MustBeTokenOwner,333        /// Item balance not enough.334        TokenValueTooLow,335        /// Size of item is too large.336        NftSizeLimitExceeded,337        /// No approve found338        ApproveNotFound,339        /// Requested value more than approved.340        TokenValueNotEnough,341        /// Only approved addresses can call this method.342        ApproveRequired,343        /// Address is not in white list.344        AddresNotInWhiteList,345        /// Number of collection admins bound exceeded.346        CollectionAdminsLimitExceeded,347        /// Owned tokens by a single address bound exceeded.348        AddressOwnershipLimitExceeded,349        /// Length of items properties must be greater than 0.350        EmptyArgument,351        /// const_data exceeded data limit.352        TokenConstDataLimitExceeded,353        /// variable_data exceeded data limit.354        TokenVariableDataLimitExceeded,355        /// Not NFT item data used to mint in NFT collection.356        NotNftDataUsedToMintNftCollectionToken,357        /// Not Fungible item data used to mint in Fungible collection.358        NotFungibleDataUsedToMintFungibleCollectionToken,359        /// Not Re Fungible item data used to mint in Re Fungible collection.360        NotReFungibleDataUsedToMintReFungibleCollectionToken,361        /// Unexpected collection type.362        UnexpectedCollectionType,363        /// Can't store metadata in fungible tokens.364        CantStoreMetadataInFungibleTokens,365        /// Collection token limit exceeded366        CollectionTokenLimitExceeded,367        /// Account token limit exceeded per collection368        AccountTokenLimitExceeded,369        /// Collection limit bounds per collection exceeded370        CollectionLimitBoundsExceeded371	}372}373374pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {375    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;376377    /// Weight information for extrinsics in this pallet.378	type WeightInfo: WeightInfo;379}380381#[cfg(feature = "runtime-benchmarks")]382mod benchmarking;383384// #endregion385386decl_storage! {387    trait Store for Module<T: Trait> as Nft {388389        // Private members390        NextCollectionID: CollectionId;391        CreatedCollectionCount: u32;392        ChainVersion: u64;393        ItemListIndex: map hasher(identity) CollectionId => TokenId;394395        // Chain limits struct396        pub ChainLimit get(fn chain_limit) config(): ChainLimits;397398        // Bound counters399        CollectionCount: u32;400        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;401402        // Basic collections403        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;404        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;405        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;406407        /// Balance owner per collection map408        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;409410        /// second parameter: item id + owner account id + spender account id411        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;412413        /// Item collections414        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;415        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;416        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;417418        /// Index list419        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;420421        /// Tokens transfer baskets422        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;423        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;424        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;425426        // Contract Sponsorship and Ownership427        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;428        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;429        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;430        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;431        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 432        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 433    }434    add_extra_genesis {435        build(|config: &GenesisConfig<T>| {436            // Modification of storage437            for (_num, _c) in &config.collection {438                <Module<T>>::init_collection(_c);439            }440441            for (_num, _c, _i) in &config.nft_item_id {442                <Module<T>>::init_nft_token(*_c, _i);443            }444445            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {446                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);447            }448449            for (_num, _c, _i) in &config.refungible_item_id {450                <Module<T>>::init_refungible_token(*_c, _i);451            }452        })453    }454}455456decl_event!(457    pub enum Event<T>458    where459        AccountId = <T as system::Trait>::AccountId,460    {461        /// New collection was created462        /// 463        /// # Arguments464        /// 465        /// * collection_id: Globally unique identifier of newly created collection.466        /// 467        /// * mode: [CollectionMode] converted into u8.468        /// 469        /// * account_id: Collection owner.470        Created(CollectionId, u8, AccountId),471472        /// New item was created.473        /// 474        /// # Arguments475        /// 476        /// * collection_id: Id of the collection where item was created.477        /// 478        /// * item_id: Id of an item. Unique within the collection.479        ItemCreated(CollectionId, TokenId),480481        /// Collection item was burned.482        /// 483        /// # Arguments484        /// 485        /// collection_id.486        /// 487        /// item_id: Identifier of burned NFT.488        ItemDestroyed(CollectionId, TokenId),489    }490);491492decl_module! {493    pub struct Module<T: Trait> for enum Call where origin: T::Origin {494495        fn deposit_event() = default;496        type Error = Error<T>;497498        fn on_initialize(now: T::BlockNumber) -> Weight {499500            if ChainVersion::get() < 2501            {502                let value = NextCollectionID::get();503                CreatedCollectionCount::put(value);504                ChainVersion::put(2);505            }506507            0508        }509510        /// 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.511        /// 512        /// # Permissions513        /// 514        /// * Anyone.515        /// 516        /// # Arguments517        /// 518        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.519        /// 520        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.521        /// 522        /// * token_prefix: UTF-8 string with token prefix.523        /// 524        /// * mode: [CollectionMode] collection type and type dependent data.525        // returns collection ID526        #[weight = T::WeightInfo::create_collection()]527        pub fn create_collection(origin,528                                 collection_name: Vec<u16>,529                                 collection_description: Vec<u16>,530                                 token_prefix: Vec<u8>,531                                 mode: CollectionMode) -> DispatchResult {532533            // Anyone can create a collection534            let who = ensure_signed(origin)?;535536            let decimal_points = match mode {537                CollectionMode::Fungible(points) => points,538                CollectionMode::ReFungible(points) => points,539                _ => 0540            };541542            // bound Total number of collections543            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);544545            // check params546            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);547            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);548            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);549            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);550551            // Generate next collection ID552            let next_id = CreatedCollectionCount::get()553                .checked_add(1)554                .ok_or(Error::<T>::NumOverflow)?;555556            // bound counter557            let total = CollectionCount::get()558                .checked_add(1)559                .ok_or(Error::<T>::NumOverflow)?;560561            CreatedCollectionCount::put(next_id);562            CollectionCount::put(total);563564            // Create new collection565            let new_collection = CollectionType {566                owner: who.clone(),567                name: collection_name,568                mode: mode.clone(),569                mint_mode: false,570                access: AccessMode::Normal,571                description: collection_description,572                decimal_points: decimal_points,573                token_prefix: token_prefix,574                offchain_schema: Vec::new(),575                schema_version: SchemaVersion::ImageURL,576                sponsor: T::AccountId::default(),577                sponsor_confirmed: false,578                variable_on_chain_schema: Vec::new(),579                const_on_chain_schema: Vec::new(),580                limits: CollectionLimits::default(),581            };582583            // Add new collection to map584            <Collection<T>>::insert(next_id, new_collection);585586            // call event587            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));588589            Ok(())590        }591592        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.593        /// 594        /// # Permissions595        /// 596        /// * Collection Owner.597        /// 598        /// # Arguments599        /// 600        /// * collection_id: collection to destroy.601        #[weight = T::WeightInfo::destroy_collection()]602        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {603604            let sender = ensure_signed(origin)?;605            Self::check_owner_permissions(collection_id, sender)?;606607            <AddressTokens<T>>::remove_prefix(collection_id);608            <Allowances<T>>::remove_prefix(collection_id);609            <Balance<T>>::remove_prefix(collection_id);610            <ItemListIndex>::remove(collection_id);611            <AdminList<T>>::remove(collection_id);612            <Collection<T>>::remove(collection_id);613            <WhiteList<T>>::remove_prefix(collection_id);614615            <NftItemList<T>>::remove_prefix(collection_id);616            <FungibleItemList<T>>::remove_prefix(collection_id);617            <ReFungibleItemList<T>>::remove_prefix(collection_id);618619            <NftTransferBasket<T>>::remove_prefix(collection_id);620            <FungibleTransferBasket<T>>::remove_prefix(collection_id);621            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);622623            if CollectionCount::get() > 0624            {625                // bound couter626                let total = CollectionCount::get()627                    .checked_sub(1)628                    .ok_or(Error::<T>::NumOverflow)?;629630                CollectionCount::put(total);631            }632633            Ok(())634        }635636        /// Add an address to white list.637        /// 638        /// # Permissions639        /// 640        /// * Collection Owner641        /// * Collection Admin642        /// 643        /// # Arguments644        /// 645        /// * collection_id.646        /// 647        /// * address.648        #[weight = T::WeightInfo::add_to_white_list()]649        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{650651            let sender = ensure_signed(origin)?;652            Self::check_owner_or_admin_permissions(collection_id, sender)?;653654            <WhiteList<T>>::insert(collection_id, address, true);655            656            Ok(())657        }658659        /// Remove an address from white list.660        /// 661        /// # Permissions662        /// 663        /// * Collection Owner664        /// * Collection Admin665        /// 666        /// # Arguments667        /// 668        /// * collection_id.669        /// 670        /// * address.671        #[weight = T::WeightInfo::remove_from_white_list()]672        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{673674            let sender = ensure_signed(origin)?;675            Self::check_owner_or_admin_permissions(collection_id, sender)?;676677            <WhiteList<T>>::remove(collection_id, address);678679            Ok(())680        }681682        /// Toggle between normal and white list access for the methods with access for `Anyone`.683        /// 684        /// # Permissions685        /// 686        /// * Collection Owner.687        /// 688        /// # Arguments689        /// 690        /// * collection_id.691        /// 692        /// * mode: [AccessMode]693        #[weight = T::WeightInfo::set_public_access_mode()]694        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult695        {696            let sender = ensure_signed(origin)?;697698            Self::check_owner_permissions(collection_id, sender)?;699            let mut target_collection = <Collection<T>>::get(collection_id);700            target_collection.access = mode;701            <Collection<T>>::insert(collection_id, target_collection);702703            Ok(())704        }705706        /// Allows Anyone to create tokens if:707        /// * White List is enabled, and708        /// * Address is added to white list, and709        /// * This method was called with True parameter710        /// 711        /// # Permissions712        /// * Collection Owner713        ///714        /// # Arguments715        /// 716        /// * collection_id.717        /// 718        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.719        #[weight = T::WeightInfo::set_mint_permission()]720        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult721        {722            let sender = ensure_signed(origin)?;723724            Self::check_owner_permissions(collection_id, sender)?;725            let mut target_collection = <Collection<T>>::get(collection_id);726            target_collection.mint_mode = mint_permission;727            <Collection<T>>::insert(collection_id, target_collection);728729            Ok(())730        }731732        /// Change the owner of the collection.733        /// 734        /// # Permissions735        /// 736        /// * Collection Owner.737        /// 738        /// # Arguments739        /// 740        /// * collection_id.741        /// 742        /// * new_owner.743        #[weight = T::WeightInfo::change_collection_owner()]744        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {745746            let sender = ensure_signed(origin)?;747            Self::check_owner_permissions(collection_id, sender)?;748            let mut target_collection = <Collection<T>>::get(collection_id);749            target_collection.owner = new_owner;750            <Collection<T>>::insert(collection_id, target_collection);751752            Ok(())753        }754755        /// Adds an admin of the Collection.756        /// 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. 757        /// 758        /// # Permissions759        /// 760        /// * Collection Owner.761        /// * Collection Admin.762        /// 763        /// # Arguments764        /// 765        /// * collection_id: ID of the Collection to add admin for.766        /// 767        /// * new_admin_id: Address of new admin to add.768        #[weight = T::WeightInfo::add_collection_admin()]769        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {770771            let sender = ensure_signed(origin)?;772            Self::check_owner_or_admin_permissions(collection_id, sender)?;773            let mut admin_arr: Vec<T::AccountId> = Vec::new();774775            if <AdminList<T>>::contains_key(collection_id)776            {777                admin_arr = <AdminList<T>>::get(collection_id);778                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);779            }780781            // Number of collection admins782            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);783784            admin_arr.push(new_admin_id);785            <AdminList<T>>::insert(collection_id, admin_arr);786787            Ok(())788        }789790        /// 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.791        ///792        /// # Permissions793        /// 794        /// * Collection Owner.795        /// * Collection Admin.796        /// 797        /// # Arguments798        /// 799        /// * collection_id: ID of the Collection to remove admin for.800        /// 801        /// * account_id: Address of admin to remove.802        #[weight = T::WeightInfo::remove_collection_admin()]803        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {804805            let sender = ensure_signed(origin)?;806            Self::check_owner_or_admin_permissions(collection_id, sender)?;807808            if <AdminList<T>>::contains_key(collection_id)809            {810                let mut admin_arr = <AdminList<T>>::get(collection_id);811                admin_arr.retain(|i| *i != account_id);812                <AdminList<T>>::insert(collection_id, admin_arr);813            }814815            Ok(())816        }817818        /// # Permissions819        /// 820        /// * Collection Owner821        /// 822        /// # Arguments823        /// 824        /// * collection_id.825        /// 826        /// * new_sponsor.827        #[weight = T::WeightInfo::set_collection_sponsor()]828        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {829830            let sender = ensure_signed(origin)?;831            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);832833            let mut target_collection = <Collection<T>>::get(collection_id);834            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);835836            target_collection.sponsor = new_sponsor;837            target_collection.sponsor_confirmed = false;838            <Collection<T>>::insert(collection_id, target_collection);839840            Ok(())841        }842843        /// # Permissions844        /// 845        /// * Sponsor.846        /// 847        /// # Arguments848        /// 849        /// * collection_id.850        #[weight = T::WeightInfo::confirm_sponsorship()]851        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {852853            let sender = ensure_signed(origin)?;854            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);855856            let mut target_collection = <Collection<T>>::get(collection_id);857            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);858859            target_collection.sponsor_confirmed = true;860            <Collection<T>>::insert(collection_id, target_collection);861862            Ok(())863        }864865        /// Switch back to pay-per-own-transaction model.866        ///867        /// # Permissions868        ///869        /// * Collection owner.870        /// 871        /// # Arguments872        /// 873        /// * collection_id.874        #[weight = T::WeightInfo::remove_collection_sponsor()]875        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {876877            let sender = ensure_signed(origin)?;878            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);879880            let mut target_collection = <Collection<T>>::get(collection_id);881            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);882883            target_collection.sponsor = T::AccountId::default();884            target_collection.sponsor_confirmed = false;885            <Collection<T>>::insert(collection_id, target_collection);886887            Ok(())888        }889890        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.891        /// 892        /// # Permissions893        /// 894        /// * Collection Owner.895        /// * Collection Admin.896        /// * Anyone if897        ///     * White List is enabled, and898        ///     * Address is added to white list, and899        ///     * MintPermission is enabled (see SetMintPermission method)900        /// 901        /// # Arguments902        /// 903        /// * collection_id: ID of the collection.904        /// 905        /// * owner: Address, initial owner of the NFT.906        ///907        /// * data: Token data to store on chain.908        // #[weight =909        // (130_000_000 as Weight)910        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))911        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))912        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]913914        #[weight = T::WeightInfo::create_item(data.len())]915        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {916917            let sender = ensure_signed(origin)?;918919            Self::collection_exists(collection_id)?;920921            let target_collection = <Collection<T>>::get(collection_id);922923            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;924            Self::validate_create_item_args(&target_collection, &data)?;925            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;926927            Ok(())928        }929930        /// This method creates multiple instances of NFT Collection created with CreateCollection method.931        /// 932        /// # Permissions933        /// 934        /// * Collection Owner.935        /// * Collection Admin.936        /// * Anyone if937        ///     * White List is enabled, and938        ///     * Address is added to white list, and939        ///     * MintPermission is enabled (see SetMintPermission method)940        /// 941        /// # Arguments942        /// 943        /// * collection_id: ID of the collection.944        /// 945        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].946        /// 947        /// * owner: Address, initial owner of the NFT.948        #[weight = T::WeightInfo::create_item(items_data.into_iter()949                               .map(|data| { data.len() })950                               .sum())]951        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {952953            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);954            let sender = ensure_signed(origin)?;955956            Self::collection_exists(collection_id)?;957            let target_collection = <Collection<T>>::get(collection_id);958959            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;960961            for data in &items_data {962                Self::validate_create_item_args(&target_collection, data)?;963            }964            for data in &items_data {965                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;966            }967968            Ok(())969        }970971        /// Destroys a concrete instance of NFT.972        /// 973        /// # Permissions974        /// 975        /// * Collection Owner.976        /// * Collection Admin.977        /// * Current NFT Owner.978        /// 979        /// # Arguments980        /// 981        /// * collection_id: ID of the collection.982        /// 983        /// * item_id: ID of NFT to burn.984        #[weight = T::WeightInfo::burn_item()]985        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {986987            let sender = ensure_signed(origin)?;988            Self::collection_exists(collection_id)?;989990            // Transfer permissions check991            let target_collection = <Collection<T>>::get(collection_id);992            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||993                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),994                Error::<T>::NoPermission);995996            if target_collection.access == AccessMode::WhiteList {997                Self::check_white_list(collection_id, &sender)?;998            }9991000            match target_collection.mode1001            {1002                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1003                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1004                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1005                _ => ()1006            };10071008            // call event1009            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10101011            Ok(())1012        }10131014        /// Change ownership of the token.1015        /// 1016        /// # Permissions1017        /// 1018        /// * Collection Owner1019        /// * Collection Admin1020        /// * Current NFT owner1021        ///1022        /// # Arguments1023        /// 1024        /// * recipient: Address of token recipient.1025        /// 1026        /// * collection_id.1027        /// 1028        /// * item_id: ID of the item1029        ///     * Non-Fungible Mode: Required.1030        ///     * Fungible Mode: Ignored.1031        ///     * Re-Fungible Mode: Required.1032        /// 1033        /// * value: Amount to transfer.1034        ///     * Non-Fungible Mode: Ignored1035        ///     * Fungible Mode: Must specify transferred amount1036        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1037        #[weight = T::WeightInfo::transfer()]1038        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10391040            let sender = ensure_signed(origin)?;1041            let target_collection = <Collection<T>>::get(collection_id);10421043            // Limits check1044            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;10451046            // Transfer permissions check1047            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1048                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1049                Error::<T>::NoPermission);10501051            if target_collection.access == AccessMode::WhiteList {1052                Self::check_white_list(collection_id, &sender)?;1053                Self::check_white_list(collection_id, &recipient)?;1054            }10551056            match target_collection.mode1057            {1058                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1059                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1060                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1061                _ => ()1062            };10631064            Ok(())1065        }10661067        /// Set, change, or remove approved address to transfer the ownership of the NFT.1068        /// 1069        /// # Permissions1070        /// 1071        /// * Collection Owner1072        /// * Collection Admin1073        /// * Current NFT owner1074        /// 1075        /// # Arguments1076        /// 1077        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1078        /// 1079        /// * collection_id.1080        /// 1081        /// * item_id: ID of the item.1082        #[weight = T::WeightInfo::approve()]1083        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {10841085            let sender = ensure_signed(origin)?;10861087            // Transfer permissions check1088            let target_collection = <Collection<T>>::get(collection_id);1089            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1090                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1091                Error::<T>::NoPermission);10921093            if target_collection.access == AccessMode::WhiteList {1094                Self::check_white_list(collection_id, &sender)?;1095                Self::check_white_list(collection_id, &spender)?;1096            }10971098            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1099            let mut allowance: u128 = amount;1100            if allowance_exists {1101                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1102            }1103            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11041105            Ok(())1106        }1107        1108        /// 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.1109        /// 1110        /// # Permissions1111        /// * Collection Owner1112        /// * Collection Admin1113        /// * Current NFT owner1114        /// * Address approved by current NFT owner1115        /// 1116        /// # Arguments1117        /// 1118        /// * from: Address that owns token.1119        /// 1120        /// * recipient: Address of token recipient.1121        /// 1122        /// * collection_id.1123        /// 1124        /// * item_id: ID of the item.1125        /// 1126        /// * value: Amount to transfer.1127        #[weight = T::WeightInfo::transfer_from()]1128        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11291130            let sender = ensure_signed(origin)?;1131            let mut appoved_transfer = false;11321133            // Check approval1134            let mut approval: u128 = 0;1135            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &recipient)) {1136                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &recipient));1137                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1138                appoved_transfer = true;1139            }11401141            let target_collection = <Collection<T>>::get(collection_id);11421143            // Limits check1144            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11451146            // Transfer permissions check         1147            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1148                Error::<T>::NoPermission);11491150            if target_collection.access == AccessMode::WhiteList {1151                Self::check_white_list(collection_id, &sender)?;1152                Self::check_white_list(collection_id, &recipient)?;1153            }11541155            // Reduce approval by transferred amount or remove if remaining approval drops to 01156            if approval - value > 0 {1157                <Allowances<T>>::insert(collection_id, (item_id, &from, &recipient), approval - value);1158            }1159            else {1160                <Allowances<T>>::remove(collection_id, (item_id, &from, &recipient));1161            }11621163            match target_collection.mode1164            {1165                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1166                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1167                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1168                _ => ()1169            };11701171            Ok(())1172        }11731174        #[weight = 0]1175        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11761177            // let no_perm_mes = "You do not have permissions to modify this collection";1178            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1179            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1180            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11811182            // // on_nft_received  call11831184            // Self::transfer(origin, collection_id, item_id, new_owner)?;11851186            Ok(())1187        }11881189        /// Set off-chain data schema.1190        /// 1191        /// # Permissions1192        /// 1193        /// * Collection Owner1194        /// * Collection Admin1195        /// 1196        /// # Arguments1197        /// 1198        /// * collection_id.1199        /// 1200        /// * schema: String representing the offchain data schema.1201        #[weight = T::WeightInfo::set_variable_meta_data()]1202        pub fn set_variable_meta_data (1203            origin,1204            collection_id: CollectionId,1205            item_id: TokenId,1206            data: Vec<u8>1207        ) -> DispatchResult {1208            let sender = ensure_signed(origin)?;1209            1210            Self::collection_exists(collection_id)?;1211            1212            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12131214            // Modify permissions check1215            let target_collection = <Collection<T>>::get(collection_id);1216            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1217                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1218                Error::<T>::NoPermission);12191220            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12211222            match target_collection.mode1223            {1224                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1225                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1226                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1227                _ => fail!(Error::<T>::UnexpectedCollectionType)1228            };12291230            Ok(())1231        }1232 1233        /// Set schema standard1234        /// ImageURL1235        /// Unique1236        /// 1237        /// # Permissions1238        /// 1239        /// * Collection Owner1240        /// * Collection Admin1241        /// 1242        /// # Arguments1243        /// 1244        /// * collection_id.1245        /// 1246        /// * schema: SchemaVersion: enum1247        #[weight = T::WeightInfo::set_schema_version()]1248        pub fn set_schema_version(1249            origin,1250            collection_id: CollectionId,1251            version: SchemaVersion1252        ) -> DispatchResult {1253            let sender = ensure_signed(origin)?;1254            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1255            let mut target_collection = <Collection<T>>::get(collection_id);1256            target_collection.schema_version = version;1257            <Collection<T>>::insert(collection_id, target_collection);12581259            Ok(())1260        }12611262        /// Set off-chain data schema.1263        /// 1264        /// # Permissions1265        /// 1266        /// * Collection Owner1267        /// * Collection Admin1268        /// 1269        /// # Arguments1270        /// 1271        /// * collection_id.1272        /// 1273        /// * schema: String representing the offchain data schema.1274        #[weight = T::WeightInfo::set_offchain_schema()]1275        pub fn set_offchain_schema(1276            origin,1277            collection_id: CollectionId,1278            schema: Vec<u8>1279        ) -> DispatchResult {1280            let sender = ensure_signed(origin)?;1281            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12821283            let mut target_collection = <Collection<T>>::get(collection_id);1284            target_collection.offchain_schema = schema;1285            <Collection<T>>::insert(collection_id, target_collection);12861287            Ok(())1288        }12891290        /// Set const on-chain data schema.1291        /// 1292        /// # Permissions1293        /// 1294        /// * Collection Owner1295        /// * Collection Admin1296        /// 1297        /// # Arguments1298        /// 1299        /// * collection_id.1300        /// 1301        /// * schema: String representing the const on-chain data schema.1302        #[weight = T::WeightInfo::set_const_on_chain_schema()]1303        pub fn set_const_on_chain_schema (1304            origin,1305            collection_id: CollectionId,1306            schema: Vec<u8>1307        ) -> DispatchResult {1308            let sender = ensure_signed(origin)?;1309            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13101311            let mut target_collection = <Collection<T>>::get(collection_id);1312            target_collection.const_on_chain_schema = schema;1313            <Collection<T>>::insert(collection_id, target_collection);13141315            Ok(())1316        }13171318        /// Set variable on-chain data schema.1319        /// 1320        /// # Permissions1321        /// 1322        /// * Collection Owner1323        /// * Collection Admin1324        /// 1325        /// # Arguments1326        /// 1327        /// * collection_id.1328        /// 1329        /// * schema: String representing the variable on-chain data schema.1330        #[weight = T::WeightInfo::set_const_on_chain_schema()]1331        pub fn set_variable_on_chain_schema (1332            origin,1333            collection_id: CollectionId,1334            schema: Vec<u8>1335        ) -> DispatchResult {1336            let sender = ensure_signed(origin)?;1337            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13381339            let mut target_collection = <Collection<T>>::get(collection_id);1340            target_collection.variable_on_chain_schema = schema;1341            <Collection<T>>::insert(collection_id, target_collection);13421343            Ok(())1344        }13451346        // Sudo permissions function1347        #[weight = T::WeightInfo::set_chain_limits()]1348        pub fn set_chain_limits(1349            origin,1350            limits: ChainLimits1351        ) -> DispatchResult {13521353            #[cfg(not(feature = "runtime-benchmarks"))]1354            ensure_root(origin)?;13551356            <ChainLimit>::put(limits);1357            Ok(())1358        }13591360        /// Enable smart contract self-sponsoring.1361        /// 1362        /// # Permissions1363        /// 1364        /// * Contract Owner1365        /// 1366        /// # Arguments1367        /// 1368        /// * contract address1369        /// * enable flag1370        /// 1371        #[weight = T::WeightInfo::enable_contract_sponsoring()]1372        pub fn enable_contract_sponsoring(1373            origin,1374            contract_address: T::AccountId,1375            enable: bool1376        ) -> DispatchResult {13771378            let sender = ensure_signed(origin)?;13791380            #[cfg(feature = "runtime-benchmarks")]1381            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13821383            Self::ensure_contract_owned(sender, &contract_address)?;13841385            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1386            Ok(())1387        }13881389        /// Set the rate limit for contract sponsoring to specified number of blocks.1390        /// 1391        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1392        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1393        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1394        /// from contract endowment if there are at least B blocks between such transactions. 1395        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1396        /// 1397        /// # Permissions1398        /// 1399        /// * Contract Owner1400        /// 1401        /// # Arguments1402        /// 1403        /// -`contract_address`: Address of the contract to sponsor1404        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1405        /// 1406        #[weight = T::WeightInfo::set_contract_sponsoring_rate_limit()]1407        pub fn set_contract_sponsoring_rate_limit(1408            origin,1409            contract_address: T::AccountId,1410            rate_limit: T::BlockNumber1411        ) -> DispatchResult {1412            let sender = ensure_signed(origin)?;14131414            #[cfg(feature = "runtime-benchmarks")]1415            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14161417            Self::ensure_contract_owned(sender, &contract_address)?;1418            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1419            Ok(())1420        }14211422        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1423        /// 1424        /// # Permissions1425        /// 1426        /// * Address that deployed smart contract.1427        /// 1428        /// # Arguments1429        /// 1430        /// -`contract_address`: Address of the contract.1431        /// 1432        /// - `enable`: .  1433        #[weight = T::WeightInfo::toggle_contract_white_list()]1434        pub fn toggle_contract_white_list(1435            origin,1436            contract_address: T::AccountId,1437            enable: bool1438        ) -> DispatchResult {1439            let sender = ensure_signed(origin)?;14401441            #[cfg(feature = "runtime-benchmarks")]1442            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14431444            Self::ensure_contract_owned(sender, &contract_address)?;1445            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1446            Ok(())1447        }1448        1449        /// Add an address to smart contract white list.1450        /// 1451        /// # Permissions1452        /// 1453        /// * Address that deployed smart contract.1454        /// 1455        /// # Arguments1456        /// 1457        /// -`contract_address`: Address of the contract.1458        ///1459        /// -`account_address`: Address to add.1460        #[weight = T::WeightInfo::add_to_contract_white_list()]1461        pub fn add_to_contract_white_list(1462            origin,1463            contract_address: T::AccountId,1464            account_address: T::AccountId1465        ) -> DispatchResult {1466            let sender = ensure_signed(origin)?;14671468            #[cfg(feature = "runtime-benchmarks")]1469            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1470            1471            Self::ensure_contract_owned(sender, &contract_address)?;      1472            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1473            Ok(())1474        }14751476        /// Remove an address from smart contract white list.1477        /// 1478        /// # Permissions1479        /// 1480        /// * Address that deployed smart contract.1481        /// 1482        /// # Arguments1483        /// 1484        /// -`contract_address`: Address of the contract.1485        ///1486        /// -`account_address`: Address to remove.1487        #[weight = T::WeightInfo::remove_from_contract_white_list()]1488        pub fn remove_from_contract_white_list(1489            origin,1490            contract_address: T::AccountId,1491            account_address: T::AccountId1492        ) -> DispatchResult {1493            let sender = ensure_signed(origin)?;14941495            #[cfg(feature = "runtime-benchmarks")]1496            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14971498            Self::ensure_contract_owned(sender, &contract_address)?;1499            <ContractWhiteList<T>>::remove(contract_address, account_address);1500            Ok(())1501        }15021503        #[weight = T::WeightInfo::set_collection_limits()]1504        pub fn set_collection_limits(1505            origin,1506            collection_id: u32,1507            limits: CollectionLimits,1508        ) -> DispatchResult {1509            let sender = ensure_signed(origin)?;1510            Self::check_owner_permissions(collection_id, sender.clone())?;1511            let mut target_collection = <Collection<T>>::get(collection_id);1512            let chain_limits = ChainLimit::get();1513            let climits = target_collection.limits;15141515            // collection bounds1516            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1517                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1518                Error::<T>::CollectionLimitBoundsExceeded);15191520            // token_limit   check  prev1521            ensure!(climits.token_limit > limits.token_limit && 1522                limits.token_limit <= chain_limits.account_token_ownership_limit, 1523                Error::<T>::AccountTokenLimitExceeded);15241525            target_collection.limits = limits;1526            <Collection<T>>::insert(collection_id, target_collection);15271528            Ok(())1529        } 1530    }1531}15321533impl<T: Trait> Module<T> {15341535    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15361537        // check token limit and account token limit1538        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1539        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1540        1541        Ok(())1542    }15431544    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15451546        // check token limit and account token limit1547        let total_items: u32 = ItemListIndex::get(collection_id);1548        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1549        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1550        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15511552        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1553            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1554            Self::check_white_list(collection_id, owner)?;1555            Self::check_white_list(collection_id, sender)?;1556        }15571558        Ok(())1559    }15601561    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1562        match target_collection.mode1563        {1564            CollectionMode::NFT => {1565                if let CreateItemData::NFT(data) = data {1566                    // check sizes1567                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1568                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1569                } else {1570                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1571                }1572            },1573            CollectionMode::Fungible(_) => {1574                if let CreateItemData::Fungible(_) = data {1575                } else {1576                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1577                }1578            },1579            CollectionMode::ReFungible(_) => {1580                if let CreateItemData::ReFungible(data) = data {15811582                    // check sizes1583                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1584                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1585                } else {1586                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1587                }1588            },1589            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1590        };15911592        Ok(())1593    }15941595    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1596        match data1597        {1598            CreateItemData::NFT(data) => {1599                let item = NftItemType {1600                    owner,1601                    const_data: data.const_data,1602                    variable_data: data.variable_data1603                };16041605                Self::add_nft_item(collection_id, item)?;1606            },1607            CreateItemData::Fungible(data) => {1608                Self::add_fungible_item(collection_id, &owner, data.value)?;1609            },1610            CreateItemData::ReFungible(data) => {1611                let mut owner_list = Vec::new();1612                let value = (10 as u128).pow(collection.decimal_points as u32);1613                owner_list.push(Ownership {owner: owner.clone(), fraction: value});16141615                let item = ReFungibleItemType {1616                    owner: owner_list,1617                    const_data: data.const_data,1618                    variable_data: data.variable_data1619                };16201621                Self::add_refungible_item(collection_id, item)?;1622            }1623        };16241625        // call event1626        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16271628        Ok(())1629    }16301631    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {16321633        // Does new owner already have an account?1634        let mut balance: u128 = 0;1635        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1636            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1637        } 16381639        // Mint 1640        let item = FungibleItemType {1641            value: balance + value1642        };1643        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16441645        // Update balance1646        let new_balance = <Balance<T>>::get(collection_id, owner)1647            .checked_add(value)1648            .ok_or(Error::<T>::NumOverflow)?;1649        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);16501651        Ok(())1652    }16531654    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1655        let current_index = <ItemListIndex>::get(collection_id)1656            .checked_add(1)1657            .ok_or(Error::<T>::NumOverflow)?;1658        let itemcopy = item.clone();16591660        let value = item.owner.first().unwrap().fraction;1661        let owner = item.owner.first().unwrap().owner.clone();16621663        Self::add_token_index(collection_id, current_index, owner.clone())?;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.clone())1670            .checked_add(value)1671            .ok_or(Error::<T>::NumOverflow)?;1672        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);16731674        Ok(())1675    }16761677    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1678        let current_index = <ItemListIndex>::get(collection_id)1679            .checked_add(1)1680            .ok_or(Error::<T>::NumOverflow)?;16811682        let item_owner = item.owner.clone();1683        Self::add_token_index(collection_id, current_index, item.owner.clone())?;16841685        <ItemListIndex>::insert(collection_id, current_index);1686        <NftItemList<T>>::insert(collection_id, current_index, item);16871688        // Update balance1689        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1690            .checked_add(1)1691            .ok_or(Error::<T>::NumOverflow)?;1692        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);16931694        Ok(())1695    }16961697    fn burn_refungible_item(1698        collection_id: CollectionId,1699        item_id: TokenId,1700        owner: T::AccountId,1701    ) -> DispatchResult {1702        ensure!(1703            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1704            Error::<T>::TokenNotFound1705        );1706        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1707        let item = collection1708            .owner1709            .iter()1710            .filter(|&i| i.owner == owner)1711            .next()1712            .unwrap();1713        Self::remove_token_index(collection_id, item_id, owner.clone())?;17141715        // update balance1716        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1717            .checked_sub(item.fraction)1718            .ok_or(Error::<T>::NumOverflow)?;1719        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17201721        <ReFungibleItemList<T>>::remove(collection_id, item_id);17221723        Ok(())1724    }17251726    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1727        ensure!(1728            <NftItemList<T>>::contains_key(collection_id, item_id),1729            Error::<T>::TokenNotFound1730        );1731        let item = <NftItemList<T>>::get(collection_id, item_id);1732        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17331734        // update balance1735        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1736            .checked_sub(1)1737            .ok_or(Error::<T>::NumOverflow)?;1738        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1739        <NftItemList<T>>::remove(collection_id, item_id);17401741        Ok(())1742    }17431744    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1745        ensure!(1746            <FungibleItemList<T>>::contains_key(collection_id, owner),1747            Error::<T>::TokenNotFound1748        );1749        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1750        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);17511752        // update balance1753        let new_balance = <Balance<T>>::get(collection_id, owner)1754            .checked_sub(value)1755            .ok_or(Error::<T>::NumOverflow)?;1756        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17571758        if balance.value - value > 0 {1759            balance.value -= value;1760            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1761        }1762        else {1763            <FungibleItemList<T>>::remove(collection_id, owner);1764        }17651766        Ok(())1767    }17681769    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1770        ensure!(1771            <Collection<T>>::contains_key(collection_id),1772            Error::<T>::CollectionNotFound1773        );1774        Ok(())1775    }17761777    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1778        Self::collection_exists(collection_id)?;17791780        let target_collection = <Collection<T>>::get(collection_id);1781        ensure!(1782            subject == target_collection.owner,1783            Error::<T>::NoPermission1784        );17851786        Ok(())1787    }17881789    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1790        let target_collection = <Collection<T>>::get(collection_id);1791        let mut result: bool = subject == target_collection.owner;1792        let exists = <AdminList<T>>::contains_key(collection_id);17931794        if !result & exists {1795            if <AdminList<T>>::get(collection_id).contains(&subject) {1796                result = true1797            }1798        }17991800        result1801    }18021803    fn check_owner_or_admin_permissions(1804        collection_id: CollectionId,1805        subject: T::AccountId,1806    ) -> DispatchResult {1807        Self::collection_exists(collection_id)?;1808        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18091810        ensure!(1811            result,1812            Error::<T>::NoPermission1813        );1814        Ok(())1815    }18161817    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1818        let target_collection = <Collection<T>>::get(collection_id);18191820        match target_collection.mode {1821            CollectionMode::NFT => {1822                <NftItemList<T>>::get(collection_id, item_id).owner == subject1823            }1824            CollectionMode::Fungible(_) => {1825                <FungibleItemList<T>>::contains_key(collection_id, &subject)1826            }1827            CollectionMode::ReFungible(_) => {1828                <ReFungibleItemList<T>>::get(collection_id, item_id)1829                    .owner1830                    .iter()1831                    .any(|i| i.owner == subject)1832            }1833            CollectionMode::Invalid => false,1834        }1835    }18361837    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1838        let mes = Error::<T>::AddresNotInWhiteList;1839        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);18401841        Ok(())1842    }18431844    fn transfer_fungible(1845        collection_id: CollectionId,1846        value: u128,1847        owner: &T::AccountId,1848        recipient: &T::AccountId,1849    ) -> DispatchResult {1850        ensure!(1851            <FungibleItemList<T>>::contains_key(collection_id, owner),1852            Error::<T>::TokenNotFound1853        );18541855        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1856        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);18571858        // Send balance to recipient (updates balanceOf of recipient)1859        Self::add_fungible_item(collection_id, recipient, value)?;18601861        // update balanceOf of sender1862        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);18631864        // Reduce or remove sender1865        if balance.value == value {1866            <FungibleItemList<T>>::remove(collection_id, owner);1867        }1868        else {1869            balance.value -= value;1870            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1871        }18721873        Ok(())1874    }18751876    fn transfer_refungible(1877        collection_id: CollectionId,1878        item_id: TokenId,1879        value: u128,1880        owner: T::AccountId,1881        new_owner: T::AccountId,1882    ) -> DispatchResult {1883        ensure!(1884            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1885            Error::<T>::TokenNotFound1886        );18871888        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1889        let item = full_item1890            .owner1891            .iter()1892            .filter(|i| i.owner == owner)1893            .next()1894            .ok_or(Error::<T>::NumOverflow)?;1895        let amount = item.fraction;18961897        ensure!(amount >= value, Error::<T>::TokenValueTooLow);18981899        // update balance1900        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1901            .checked_sub(value)1902            .ok_or(Error::<T>::NumOverflow)?;1903        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19041905        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1906            .checked_add(value)1907            .ok_or(Error::<T>::NumOverflow)?;1908        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19091910        let old_owner = item.owner.clone();1911        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19121913        // transfer1914        if amount == value && !new_owner_has_account {1915            // change owner1916            // new owner do not have account1917            let mut new_full_item = full_item.clone();1918            new_full_item1919                .owner1920                .iter_mut()1921                .find(|i| i.owner == owner)1922                .unwrap()1923                .owner = new_owner.clone();1924            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19251926            // update index collection1927            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1928        } else {1929            let mut new_full_item = full_item.clone();1930            new_full_item1931                .owner1932                .iter_mut()1933                .find(|i| i.owner == owner)1934                .unwrap()1935                .fraction -= value;19361937            // separate amount1938            if new_owner_has_account {1939                // new owner has account1940                new_full_item1941                    .owner1942                    .iter_mut()1943                    .find(|i| i.owner == new_owner)1944                    .unwrap()1945                    .fraction += value;1946            } else {1947                // new owner do not have account1948                new_full_item.owner.push(Ownership {1949                    owner: new_owner.clone(),1950                    fraction: value,1951                });1952                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1953            }19541955            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1956        }19571958        Ok(())1959    }19601961    fn transfer_nft(1962        collection_id: CollectionId,1963        item_id: TokenId,1964        sender: T::AccountId,1965        new_owner: T::AccountId,1966    ) -> DispatchResult {1967        ensure!(1968            <NftItemList<T>>::contains_key(collection_id, item_id),1969            Error::<T>::TokenNotFound1970        );19711972        let mut item = <NftItemList<T>>::get(collection_id, item_id);19731974        ensure!(1975            sender == item.owner,1976            Error::<T>::MustBeTokenOwner1977        );19781979        // update balance1980        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1981            .checked_sub(1)1982            .ok_or(Error::<T>::NumOverflow)?;1983        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19841985        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1986            .checked_add(1)1987            .ok_or(Error::<T>::NumOverflow)?;1988        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19891990        // change owner1991        let old_owner = item.owner.clone();1992        item.owner = new_owner.clone();1993        <NftItemList<T>>::insert(collection_id, item_id, item);19941995        // update index collection1996        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;19971998        Ok(())1999    }2000    2001    fn item_exists(2002        collection_id: CollectionId,2003        item_id: TokenId,2004        mode: &CollectionMode2005    ) -> DispatchResult {2006        match mode {2007            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2008            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2009            _ => ()2010        };2011        2012        Ok(())2013    }20142015    fn set_re_fungible_variable_data(2016        collection_id: CollectionId,2017        item_id: TokenId,2018        data: Vec<u8>2019    ) -> DispatchResult {2020        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20212022        item.variable_data = data;20232024        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20252026        Ok(())2027    }20282029    fn set_nft_variable_data(2030        collection_id: CollectionId,2031        item_id: TokenId,2032        data: Vec<u8>2033    ) -> DispatchResult {2034        let mut item = <NftItemList<T>>::get(collection_id, item_id);2035        2036        item.variable_data = data;20372038        <NftItemList<T>>::insert(collection_id, item_id, item);2039        2040        Ok(())2041    }20422043    fn init_collection(item: &CollectionType<T::AccountId>) {2044        // check params2045        assert!(2046            item.decimal_points <= MAX_DECIMAL_POINTS,2047            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2048        );2049        assert!(2050            item.name.len() <= 64,2051            "Collection name can not be longer than 63 char"2052        );2053        assert!(2054            item.name.len() <= 256,2055            "Collection description can not be longer than 255 char"2056        );2057        assert!(2058            item.token_prefix.len() <= 16,2059            "Token prefix can not be longer than 15 char"2060        );20612062        // Generate next collection ID2063        let next_id = CreatedCollectionCount::get()2064            .checked_add(1)2065            .unwrap();20662067        CreatedCollectionCount::put(next_id);2068    }20692070    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2071        let current_index = <ItemListIndex>::get(collection_id)2072            .checked_add(1)2073            .unwrap();20742075        let item_owner = item.owner.clone();2076        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();20772078        <ItemListIndex>::insert(collection_id, current_index);20792080        // Update balance2081        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2082            .checked_add(1)2083            .unwrap();2084        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2085    }20862087    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2088        let current_index = <ItemListIndex>::get(collection_id)2089            .checked_add(1)2090            .unwrap();20912092        Self::add_token_index(collection_id, current_index, (*owner).clone()).unwrap();20932094        <ItemListIndex>::insert(collection_id, current_index);20952096        // Update balance2097        let new_balance = <Balance<T>>::get(collection_id, owner)2098            .checked_add(item.value)2099            .unwrap();2100        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2101    }21022103    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2104        let current_index = <ItemListIndex>::get(collection_id)2105            .checked_add(1)2106            .unwrap();21072108        let value = item.owner.first().unwrap().fraction;2109        let owner = item.owner.first().unwrap().owner.clone();21102111        Self::add_token_index(collection_id, current_index, owner.clone()).unwrap();21122113        <ItemListIndex>::insert(collection_id, current_index);21142115        // Update balance2116        let new_balance = <Balance<T>>::get(collection_id, owner.clone())2117            .checked_add(value)2118            .unwrap();2119        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2120    }21212122    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {21232124        // add to account limit2125        if <AccountItemCount<T>>::contains_key(owner.clone()) {21262127            // bound Owned tokens by a single address2128            let count = <AccountItemCount<T>>::get(owner.clone());2129            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21302131            <AccountItemCount<T>>::insert(owner.clone(), count2132                .checked_add(1)2133                .ok_or(Error::<T>::NumOverflow)?);2134        }2135        else {2136            <AccountItemCount<T>>::insert(owner.clone(), 1);2137        }21382139        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2140        if list_exists {2141            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2142            let item_contains = list.contains(&item_index.clone());21432144            if !item_contains {2145                list.push(item_index.clone());2146            }21472148            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2149        } else {2150            let mut itm = Vec::new();2151            itm.push(item_index.clone());2152            <AddressTokens<T>>::insert(collection_id, owner, itm);2153            2154        }21552156        Ok(())2157    }21582159    fn remove_token_index(2160        collection_id: CollectionId,2161        item_index: TokenId,2162        owner: T::AccountId,2163    ) -> DispatchResult {21642165        // update counter2166        <AccountItemCount<T>>::insert(owner.clone(), 2167            <AccountItemCount<T>>::get(owner.clone())2168            .checked_sub(1)2169            .ok_or(Error::<T>::NumOverflow)?);217021712172        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2173        if list_exists {2174            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2175            let item_contains = list.contains(&item_index.clone());21762177            if item_contains {2178                list.retain(|&item| item != item_index);2179                <AddressTokens<T>>::insert(collection_id, owner, list);2180            }2181        }21822183        Ok(())2184    }21852186    fn move_token_index(2187        collection_id: CollectionId,2188        item_index: TokenId,2189        old_owner: T::AccountId,2190        new_owner: T::AccountId,2191    ) -> DispatchResult {2192        Self::remove_token_index(collection_id, item_index, old_owner)?;2193        Self::add_token_index(collection_id, item_index, new_owner)?;21942195        Ok(())2196    }2197    2198    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2199        if <ContractOwner<T>>::contains_key(contract.clone()) {2200            let owner = <ContractOwner<T>>::get(contract);2201            ensure!(account == owner, Error::<T>::NoPermission);2202        } else {2203            fail!(Error::<T>::NoPermission);2204        }22052206        Ok(())2207    }2208}22092210////////////////////////////////////////////////////////////////////////////////////////////////////2211// Economic models2212// #region22132214/// Fee multiplier.2215pub type Multiplier = FixedU128;22162217type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2218    <T as system::Trait>::AccountId,2219>>::Balance;2220type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2221    <T as system::Trait>::AccountId,2222>>::NegativeImbalance;22232224/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2225/// in the queue.2226#[derive(Encode, Decode, Clone, Eq, PartialEq)]2227pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2228    #[codec(compact)] BalanceOf<T>2229);22302231impl<T: Trait + Send + Sync> sp_std::fmt::Debug2232    for ChargeTransactionPayment<T>2233{2234    #[cfg(feature = "std")]2235    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2236        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2237    }2238    #[cfg(not(feature = "std"))]2239    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2240        Ok(())2241    }2242}22432244impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2245where2246    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2247    BalanceOf<T>: Send + Sync + FixedPointOperand,2248{2249    /// utility constructor. Used only in client/factory code.2250    pub fn from(fee: BalanceOf<T>) -> Self {2251        Self(fee)2252    }22532254    pub fn traditional_fee(2255        len: usize,2256        info: &DispatchInfoOf<T::Call>,2257        tip: BalanceOf<T>,2258    ) -> BalanceOf<T>2259    where2260        T::Call: Dispatchable<Info = DispatchInfo>,2261    {2262        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2263    }22642265	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2266		let weight_saturation = T::MaximumBlockWeight::get() / info.weight.max(1);2267		let len_saturation = T::MaximumBlockLength::get() as u64 / (len as u64).max(1);2268		let coefficient: BalanceOf<T> = weight_saturation.min(len_saturation).saturated_into::<BalanceOf<T>>();2269		final_fee.saturating_mul(coefficient).saturated_into::<TransactionPriority>()2270	}22712272    fn withdraw_fee(2273        &self,2274        who: &T::AccountId,2275        call: &T::Call,2276        info: &DispatchInfoOf<T::Call>,2277        len: usize,2278    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2279        let tip = self.0;22802281        // Set fee based on call type. Creating collection costs 1 Unique.2282        // All other transactions have traditional fees so far2283        // let fee = match call.is_sub_type() {2284        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2285        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2286        //                                                 // _ => <BalanceOf<T>>::from(100)2287        // };2288        let fee = Self::traditional_fee(len, info, tip);22892290        // Only mess with balances if fee is not zero.2291        if fee.is_zero() {2292            return Ok((fee, None));2293        }22942295        // Determine who is paying transaction fee based on ecnomic model2296        // Parse call to extract collection ID and access collection sponsor2297        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2298            Some(Call::create_item(collection_id, _owner, _properties)) => {22992300                // check free create limit2301                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2302                   (<Collection<T>>::get(collection_id).sponsor_confirmed)2303                {2304                    <Collection<T>>::get(collection_id).sponsor2305                } else {2306                    T::AccountId::default()2307                }2308            }2309            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2310                2311                let mut sponsor_transfer = false;2312                if <Collection<T>>::get(collection_id).sponsor_confirmed {23132314                    let collection_limits = <Collection<T>>::get(collection_id).limits;2315                    let collection_mode = <Collection<T>>::get(collection_id).mode;2316    2317                    // sponsor timeout2318                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2319                    sponsor_transfer = match collection_mode {2320                        CollectionMode::NFT => {2321    2322                            // get correct limit2323                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2324                                collection_limits.sponsor_transfer_timeout2325                            } else {2326                                ChainLimit::get().nft_sponsor_transfer_timeout2327                            };2328    2329                            let mut sponsored = true;2330                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2331                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2332                                let limit_time = last_tx_block + limit.into();2333                                if block_number <= limit_time {2334                                    sponsored = false;2335                                }2336                            }2337                            if sponsored {2338                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2339                            }23402341                            sponsored2342                        }2343                        CollectionMode::Fungible(_) => {2344    2345                            // get correct limit2346                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2347                                collection_limits.sponsor_transfer_timeout2348                            } else {2349                                ChainLimit::get().fungible_sponsor_transfer_timeout2350                            };2351    2352                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2353                            let mut sponsored = true;2354                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2355                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2356                                let limit_time = last_tx_block + limit.into();2357                                if block_number <= limit_time {2358                                    sponsored = false;2359                                }2360                            }2361                            if sponsored {2362                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2363                            }23642365                            sponsored2366                        }2367                        CollectionMode::ReFungible(_) => {2368    2369                            // get correct limit2370                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2371                                collection_limits.sponsor_transfer_timeout2372                            } else {2373                                ChainLimit::get().refungible_sponsor_transfer_timeout2374                            };2375    2376                            let mut sponsored = true;2377                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2378                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2379                                let limit_time = last_tx_block + limit.into();2380                                if block_number <= limit_time {2381                                    sponsored = false;2382                                }2383                            }2384                            if sponsored {2385                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2386                            }23872388                            sponsored2389                        }2390                        _ => {2391                            false2392                        },2393                    };2394                }23952396                if !sponsor_transfer {2397                    T::AccountId::default()2398                } else {2399                    <Collection<T>>::get(collection_id).sponsor2400                }2401            }24022403            _ => T::AccountId::default(),2404        };24052406        // Sponsor smart contracts2407        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {24082409            // On instantiation: set the contract owner2410            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {24112412                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2413                    code_hash,2414                    &data,2415                    &who,2416                );2417                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());24182419                T::AccountId::default()2420            },24212422            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2423            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {24242425                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());24262427                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2428                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2429                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2430                  2431                if !owned_contract && white_list_enabled {2432                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2433                        return Err(InvalidTransaction::Call.into());2434                    }2435                }24362437                let mut sponsor_transfer = false;2438                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2439                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2440                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2441                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2442                    let limit_time = last_tx_block + rate_limit;24432444                    if block_number >= limit_time {2445                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2446                        sponsor_transfer = true;2447                    }2448                } else {2449                    sponsor_transfer = false;2450                }2451               2452                2453                let mut sp = T::AccountId::default();2454                if sponsor_transfer {2455                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2456                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2457                            sp = called_contract;2458                        }2459                    }2460                }24612462                sp2463            },24642465            _ => sponsor,2466        };24672468        let mut who_pays_fee: T::AccountId = sponsor.clone();2469        if sponsor == T::AccountId::default() {2470            who_pays_fee = who.clone();2471        }24722473        match <T as transaction_payment::Trait>::Currency::withdraw(2474            &who_pays_fee,2475            fee,2476            if tip.is_zero() {2477                WithdrawReason::TransactionPayment.into()2478            } else {2479                WithdrawReason::TransactionPayment | WithdrawReason::Tip2480            },2481            ExistenceRequirement::KeepAlive,2482        ) {2483            Ok(imbalance) => Ok((fee, Some(imbalance))),2484            Err(_) => Err(InvalidTransaction::Payment.into()),2485        }2486    }2487}248824892490impl<T: Trait + Send + Sync> SignedExtension2491    for ChargeTransactionPayment<T>2492where2493    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2494    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2495{2496    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2497    type AccountId = T::AccountId;2498    type Call = T::Call;2499    type AdditionalSigned = ();2500    type Pre = (2501        BalanceOf<T>,2502        Self::AccountId,2503        Option<NegativeImbalanceOf<T>>,2504        BalanceOf<T>,2505    );2506    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2507        Ok(())2508    }25092510    fn validate(2511        &self,2512        who: &Self::AccountId,2513        call: &Self::Call,2514        info: &DispatchInfoOf<Self::Call>,2515        len: usize,2516    ) -> TransactionValidity {2517		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2518		Ok(ValidTransaction {2519			priority: Self::get_priority(len, info, fee),2520			..Default::default()2521		})2522    }25232524    fn pre_dispatch(2525        self,2526        who: &Self::AccountId,2527        call: &Self::Call,2528        info: &DispatchInfoOf<Self::Call>,2529        len: usize,2530    ) -> Result<Self::Pre, TransactionValidityError> {2531        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2532        Ok((self.0, who.clone(), imbalance, fee))2533    }25342535    fn post_dispatch(2536        pre: Self::Pre,2537        info: &DispatchInfoOf<Self::Call>,2538        post_info: &PostDispatchInfoOf<Self::Call>,2539        len: usize,2540        _result: &DispatchResult,2541    ) -> Result<(), TransactionValidityError> {2542        let (tip, who, imbalance, fee) = pre;2543        if let Some(payed) = imbalance {2544            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2545                len as u32, info, post_info, tip,2546            );2547            let refund = fee.saturating_sub(actual_fee);2548            let actual_payment =2549                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2550                    &who, refund,2551                ) {2552                    Ok(refund_imbalance) => {2553                        // The refund cannot be larger than the up front payed max weight.2554                        // `PostDispatchInfo::calc_unspent` guards against such a case.2555                        match payed.offset(refund_imbalance) {2556                            Ok(actual_payment) => actual_payment,2557                            Err(_) => return Err(InvalidTransaction::Payment.into()),2558                        }2559                    }2560                    // We do not recreate the account using the refund. The up front payment2561                    // is gone in that case.2562                    Err(_) => payed,2563                };2564            let imbalances = actual_payment.split(tip);2565            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2566                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2567            );2568        }2569        Ok(())2570    }2571}25722573// #endregion
after · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, WithdrawReason,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial,29    },30    IsSubType, StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use pallet_contracts::ContractAddressFor;45use sp_runtime::traits::StaticLookup;4647#[cfg(test)]48mod mock;4950#[cfg(test)]51mod tests;5253mod default_weights;5455pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;56pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;57pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;5859// Structs60// #region6162pub type CollectionId = u32;63pub type TokenId = u32;64pub type DecimalPoints = u8;6566#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]67#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]68pub enum CollectionMode {69    Invalid,70    NFT,71    // decimal points72    Fungible(DecimalPoints),73    // decimal points74    ReFungible(DecimalPoints),75}7677impl Into<u8> for CollectionMode {78    fn into(self) -> u8 {79        match self {80            CollectionMode::Invalid => 0,81            CollectionMode::NFT => 1,82            CollectionMode::Fungible(_) => 2,83            CollectionMode::ReFungible(_) => 3,84        }85    }86}8788#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]89#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]90pub enum AccessMode {91    Normal,92    WhiteList,93}94impl Default for AccessMode {95    fn default() -> Self {96        Self::Normal97    }98}99100impl Default for CollectionMode {101    fn default() -> Self {102        Self::Invalid103    }104}105106#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]107#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]108pub enum SchemaVersion {109    ImageURL,110    Unique,111}112impl Default for SchemaVersion {113    fn default() -> Self {114        Self::ImageURL115    }116}117118#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]119#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]120pub struct Ownership<AccountId> {121    pub owner: AccountId,122    pub fraction: u128,123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct CollectionType<AccountId> {128    pub owner: AccountId,129    pub mode: CollectionMode,130    pub access: AccessMode,131    pub decimal_points: DecimalPoints,132    pub name: Vec<u16>,        // 64 include null escape char133    pub description: Vec<u16>, // 256 include null escape char134    pub token_prefix: Vec<u8>, // 16 include null escape char135    pub mint_mode: bool,136    pub offchain_schema: Vec<u8>,137    pub schema_version: SchemaVersion,138    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender139    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.140    pub limits: CollectionLimits, // Collection private restrictions 141    pub variable_on_chain_schema: Vec<u8>, //142    pub const_on_chain_schema: Vec<u8>, //143}144145#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]146#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]147pub struct NftItemType<AccountId> {148    pub owner: AccountId,149    pub const_data: Vec<u8>,150    pub variable_data: Vec<u8>,151}152153#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]154#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]155pub struct FungibleItemType {156    pub value: u128,157}158159#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]160#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]161pub struct ReFungibleItemType<AccountId> {162    pub owner: Vec<Ownership<AccountId>>,163    pub const_data: Vec<u8>,164    pub variable_data: Vec<u8>,165}166167// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]168// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169// pub struct VestingItem<AccountId, Moment> {170//     pub sender: AccountId,171//     pub recipient: AccountId,172//     pub collection_id: CollectionId,173//     pub item_id: TokenId,174//     pub amount: u64,175//     pub vesting_date: Moment,176// }177178#[derive(Encode, Decode, Debug, Clone, PartialEq)]179#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]180pub struct CollectionLimits {181    pub account_token_ownership_limit: u32,182    pub sponsored_data_size: u32,183    pub token_limit: u32,184185    // Timeouts for item types in passed blocks186    pub sponsor_transfer_timeout: u32,187}188189impl Default for CollectionLimits {190    fn default() -> CollectionLimits {191        CollectionLimits { 192            account_token_ownership_limit: 10_000_000, 193            token_limit: u32::max_value(),194            sponsored_data_size: u32::max_value(), 195            sponsor_transfer_timeout: 14400 }196    }197}198199#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]200#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]201pub struct ChainLimits {202    pub collection_numbers_limit: u32,203    pub account_token_ownership_limit: u32,204    pub collections_admins_limit: u64,205    pub custom_data_limit: u32,206207    // Timeouts for item types in passed blocks208    pub nft_sponsor_transfer_timeout: u32,209    pub fungible_sponsor_transfer_timeout: u32,210    pub refungible_sponsor_transfer_timeout: u32,211}212213pub trait WeightInfo {214	fn create_collection() -> Weight;215	fn destroy_collection() -> Weight;216	fn add_to_white_list() -> Weight;217	fn remove_from_white_list() -> Weight;218    fn set_public_access_mode() -> Weight;219    fn set_mint_permission() -> Weight;220    fn change_collection_owner() -> Weight;221    fn add_collection_admin() -> Weight;222    fn remove_collection_admin() -> Weight;223    fn set_collection_sponsor() -> Weight;224    fn confirm_sponsorship() -> Weight;225    fn remove_collection_sponsor() -> Weight;226    fn create_item(s: usize) -> Weight;227    fn burn_item() -> Weight;228    fn transfer() -> Weight;229    fn approve() -> Weight;230    fn transfer_from() -> Weight;231    fn set_offchain_schema() -> Weight;232    fn set_const_on_chain_schema() -> Weight;233    fn set_variable_on_chain_schema() -> Weight;234    fn set_variable_meta_data() -> Weight;235    fn enable_contract_sponsoring() -> Weight;236    fn set_schema_version() -> Weight;237    fn set_chain_limits() -> Weight;238    fn set_contract_sponsoring_rate_limit() -> Weight;239    fn toggle_contract_white_list() -> Weight;240    fn add_to_contract_white_list() -> Weight;241    fn remove_from_contract_white_list() -> Weight;242    fn set_collection_limits() -> Weight;243}244245#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]246#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]247pub struct CreateNftData {248    pub const_data: Vec<u8>,249    pub variable_data: Vec<u8>,250}251252#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]253#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]254pub struct CreateFungibleData {255    pub value: u128,256}257258#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]259#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]260pub struct CreateReFungibleData {261    pub const_data: Vec<u8>,262    pub variable_data: Vec<u8>,263}264265#[derive(Encode, Decode, Debug, Clone, PartialEq)]266#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]267pub enum CreateItemData {268    NFT(CreateNftData),269    Fungible(CreateFungibleData),270    ReFungible(CreateReFungibleData),271}272273impl CreateItemData {274    pub fn len(&self) -> usize {275        let len = match self {276            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),277            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),278            _ => 0279        };280        281        return len;282    }283}284285impl From<CreateNftData> for CreateItemData {286    fn from(item: CreateNftData) -> Self {287        CreateItemData::NFT(item)288    }289}290291impl From<CreateReFungibleData> for CreateItemData {292    fn from(item: CreateReFungibleData) -> Self {293        CreateItemData::ReFungible(item)294    }295}296297impl From<CreateFungibleData> for CreateItemData {298    fn from(item: CreateFungibleData) -> Self {299        CreateItemData::Fungible(item)300    }301}302303304decl_error! {305	/// Error for non-fungible-token module.306	pub enum Error for Module<T: Trait> {307        /// Total collections bound exceeded.308        TotalCollectionsLimitExceeded,309		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.310        CollectionDecimalPointLimitExceeded, 311        /// Collection name can not be longer than 63 char.312        CollectionNameLimitExceeded, 313        /// Collection description can not be longer than 255 char.314        CollectionDescriptionLimitExceeded, 315        /// Token prefix can not be longer than 15 char.316        CollectionTokenPrefixLimitExceeded,317        /// This collection does not exist.318        CollectionNotFound,319        /// Item not exists.320        TokenNotFound,321        /// Arithmetic calculation overflow.322        NumOverflow,       323        /// Account already has admin role.324        AlreadyAdmin,  325        /// You do not own this collection.326        NoPermission,327        /// This address is not set as sponsor, use setCollectionSponsor first.328        ConfirmUnsetSponsorFail,329        /// Collection is not in mint mode.330        PublicMintingNotAllowed,331        /// Sender parameter and item owner must be equal.332        MustBeTokenOwner,333        /// Item balance not enough.334        TokenValueTooLow,335        /// Size of item is too large.336        NftSizeLimitExceeded,337        /// No approve found338        ApproveNotFound,339        /// Requested value more than approved.340        TokenValueNotEnough,341        /// Only approved addresses can call this method.342        ApproveRequired,343        /// Address is not in white list.344        AddresNotInWhiteList,345        /// Number of collection admins bound exceeded.346        CollectionAdminsLimitExceeded,347        /// Owned tokens by a single address bound exceeded.348        AddressOwnershipLimitExceeded,349        /// Length of items properties must be greater than 0.350        EmptyArgument,351        /// const_data exceeded data limit.352        TokenConstDataLimitExceeded,353        /// variable_data exceeded data limit.354        TokenVariableDataLimitExceeded,355        /// Not NFT item data used to mint in NFT collection.356        NotNftDataUsedToMintNftCollectionToken,357        /// Not Fungible item data used to mint in Fungible collection.358        NotFungibleDataUsedToMintFungibleCollectionToken,359        /// Not Re Fungible item data used to mint in Re Fungible collection.360        NotReFungibleDataUsedToMintReFungibleCollectionToken,361        /// Unexpected collection type.362        UnexpectedCollectionType,363        /// Can't store metadata in fungible tokens.364        CantStoreMetadataInFungibleTokens,365        /// Collection token limit exceeded366        CollectionTokenLimitExceeded,367        /// Account token limit exceeded per collection368        AccountTokenLimitExceeded,369        /// Collection limit bounds per collection exceeded370        CollectionLimitBoundsExceeded371	}372}373374pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {375    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;376377    /// Weight information for extrinsics in this pallet.378	type WeightInfo: WeightInfo;379}380381#[cfg(feature = "runtime-benchmarks")]382mod benchmarking;383384// #endregion385386decl_storage! {387    trait Store for Module<T: Trait> as Nft {388389        // Private members390        NextCollectionID: CollectionId;391        CreatedCollectionCount: u32;392        ChainVersion: u64;393        ItemListIndex: map hasher(identity) CollectionId => TokenId;394395        // Chain limits struct396        pub ChainLimit get(fn chain_limit) config(): ChainLimits;397398        // Bound counters399        CollectionCount: u32;400        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;401402        // Basic collections403        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;404        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;405        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;406407        /// Balance owner per collection map408        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;409410        /// second parameter: item id + owner account id + spender account id411        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;412413        /// Item collections414        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;415        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;416        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;417418        /// Index list419        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;420421        /// Tokens transfer baskets422        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;423        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;424        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;425426        // Contract Sponsorship and Ownership427        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;428        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;429        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;430        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;431        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 432        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 433    }434    add_extra_genesis {435        build(|config: &GenesisConfig<T>| {436            // Modification of storage437            for (_num, _c) in &config.collection {438                <Module<T>>::init_collection(_c);439            }440441            for (_num, _c, _i) in &config.nft_item_id {442                <Module<T>>::init_nft_token(*_c, _i);443            }444445            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {446                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);447            }448449            for (_num, _c, _i) in &config.refungible_item_id {450                <Module<T>>::init_refungible_token(*_c, _i);451            }452        })453    }454}455456decl_event!(457    pub enum Event<T>458    where459        AccountId = <T as system::Trait>::AccountId,460    {461        /// New collection was created462        /// 463        /// # Arguments464        /// 465        /// * collection_id: Globally unique identifier of newly created collection.466        /// 467        /// * mode: [CollectionMode] converted into u8.468        /// 469        /// * account_id: Collection owner.470        Created(CollectionId, u8, AccountId),471472        /// New item was created.473        /// 474        /// # Arguments475        /// 476        /// * collection_id: Id of the collection where item was created.477        /// 478        /// * item_id: Id of an item. Unique within the collection.479        ItemCreated(CollectionId, TokenId),480481        /// Collection item was burned.482        /// 483        /// # Arguments484        /// 485        /// collection_id.486        /// 487        /// item_id: Identifier of burned NFT.488        ItemDestroyed(CollectionId, TokenId),489    }490);491492decl_module! {493    pub struct Module<T: Trait> for enum Call where origin: T::Origin {494495        fn deposit_event() = default;496        type Error = Error<T>;497498        fn on_initialize(now: T::BlockNumber) -> Weight {499500            if ChainVersion::get() < 2501            {502                let value = NextCollectionID::get();503                CreatedCollectionCount::put(value);504                ChainVersion::put(2);505            }506507            0508        }509510        /// 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.511        /// 512        /// # Permissions513        /// 514        /// * Anyone.515        /// 516        /// # Arguments517        /// 518        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.519        /// 520        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.521        /// 522        /// * token_prefix: UTF-8 string with token prefix.523        /// 524        /// * mode: [CollectionMode] collection type and type dependent data.525        // returns collection ID526        #[weight = T::WeightInfo::create_collection()]527        pub fn create_collection(origin,528                                 collection_name: Vec<u16>,529                                 collection_description: Vec<u16>,530                                 token_prefix: Vec<u8>,531                                 mode: CollectionMode) -> DispatchResult {532533            // Anyone can create a collection534            let who = ensure_signed(origin)?;535536            let decimal_points = match mode {537                CollectionMode::Fungible(points) => points,538                CollectionMode::ReFungible(points) => points,539                _ => 0540            };541542            // bound Total number of collections543            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);544545            // check params546            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);547            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);548            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);549            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);550551            // Generate next collection ID552            let next_id = CreatedCollectionCount::get()553                .checked_add(1)554                .ok_or(Error::<T>::NumOverflow)?;555556            // bound counter557            let total = CollectionCount::get()558                .checked_add(1)559                .ok_or(Error::<T>::NumOverflow)?;560561            CreatedCollectionCount::put(next_id);562            CollectionCount::put(total);563564            // Create new collection565            let new_collection = CollectionType {566                owner: who.clone(),567                name: collection_name,568                mode: mode.clone(),569                mint_mode: false,570                access: AccessMode::Normal,571                description: collection_description,572                decimal_points: decimal_points,573                token_prefix: token_prefix,574                offchain_schema: Vec::new(),575                schema_version: SchemaVersion::ImageURL,576                sponsor: T::AccountId::default(),577                sponsor_confirmed: false,578                variable_on_chain_schema: Vec::new(),579                const_on_chain_schema: Vec::new(),580                limits: CollectionLimits::default(),581            };582583            // Add new collection to map584            <Collection<T>>::insert(next_id, new_collection);585586            // call event587            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));588589            Ok(())590        }591592        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.593        /// 594        /// # Permissions595        /// 596        /// * Collection Owner.597        /// 598        /// # Arguments599        /// 600        /// * collection_id: collection to destroy.601        #[weight = T::WeightInfo::destroy_collection()]602        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {603604            let sender = ensure_signed(origin)?;605            Self::check_owner_permissions(collection_id, sender)?;606607            <AddressTokens<T>>::remove_prefix(collection_id);608            <Allowances<T>>::remove_prefix(collection_id);609            <Balance<T>>::remove_prefix(collection_id);610            <ItemListIndex>::remove(collection_id);611            <AdminList<T>>::remove(collection_id);612            <Collection<T>>::remove(collection_id);613            <WhiteList<T>>::remove_prefix(collection_id);614615            <NftItemList<T>>::remove_prefix(collection_id);616            <FungibleItemList<T>>::remove_prefix(collection_id);617            <ReFungibleItemList<T>>::remove_prefix(collection_id);618619            <NftTransferBasket<T>>::remove_prefix(collection_id);620            <FungibleTransferBasket<T>>::remove_prefix(collection_id);621            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);622623            if CollectionCount::get() > 0624            {625                // bound couter626                let total = CollectionCount::get()627                    .checked_sub(1)628                    .ok_or(Error::<T>::NumOverflow)?;629630                CollectionCount::put(total);631            }632633            Ok(())634        }635636        /// Add an address to white list.637        /// 638        /// # Permissions639        /// 640        /// * Collection Owner641        /// * Collection Admin642        /// 643        /// # Arguments644        /// 645        /// * collection_id.646        /// 647        /// * address.648        #[weight = T::WeightInfo::add_to_white_list()]649        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{650651            let sender = ensure_signed(origin)?;652            Self::check_owner_or_admin_permissions(collection_id, sender)?;653654            <WhiteList<T>>::insert(collection_id, address, true);655            656            Ok(())657        }658659        /// Remove an address from white list.660        /// 661        /// # Permissions662        /// 663        /// * Collection Owner664        /// * Collection Admin665        /// 666        /// # Arguments667        /// 668        /// * collection_id.669        /// 670        /// * address.671        #[weight = T::WeightInfo::remove_from_white_list()]672        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{673674            let sender = ensure_signed(origin)?;675            Self::check_owner_or_admin_permissions(collection_id, sender)?;676677            <WhiteList<T>>::remove(collection_id, address);678679            Ok(())680        }681682        /// Toggle between normal and white list access for the methods with access for `Anyone`.683        /// 684        /// # Permissions685        /// 686        /// * Collection Owner.687        /// 688        /// # Arguments689        /// 690        /// * collection_id.691        /// 692        /// * mode: [AccessMode]693        #[weight = T::WeightInfo::set_public_access_mode()]694        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult695        {696            let sender = ensure_signed(origin)?;697698            Self::check_owner_permissions(collection_id, sender)?;699            let mut target_collection = <Collection<T>>::get(collection_id);700            target_collection.access = mode;701            <Collection<T>>::insert(collection_id, target_collection);702703            Ok(())704        }705706        /// Allows Anyone to create tokens if:707        /// * White List is enabled, and708        /// * Address is added to white list, and709        /// * This method was called with True parameter710        /// 711        /// # Permissions712        /// * Collection Owner713        ///714        /// # Arguments715        /// 716        /// * collection_id.717        /// 718        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.719        #[weight = T::WeightInfo::set_mint_permission()]720        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult721        {722            let sender = ensure_signed(origin)?;723724            Self::check_owner_permissions(collection_id, sender)?;725            let mut target_collection = <Collection<T>>::get(collection_id);726            target_collection.mint_mode = mint_permission;727            <Collection<T>>::insert(collection_id, target_collection);728729            Ok(())730        }731732        /// Change the owner of the collection.733        /// 734        /// # Permissions735        /// 736        /// * Collection Owner.737        /// 738        /// # Arguments739        /// 740        /// * collection_id.741        /// 742        /// * new_owner.743        #[weight = T::WeightInfo::change_collection_owner()]744        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {745746            let sender = ensure_signed(origin)?;747            Self::check_owner_permissions(collection_id, sender)?;748            let mut target_collection = <Collection<T>>::get(collection_id);749            target_collection.owner = new_owner;750            <Collection<T>>::insert(collection_id, target_collection);751752            Ok(())753        }754755        /// Adds an admin of the Collection.756        /// 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. 757        /// 758        /// # Permissions759        /// 760        /// * Collection Owner.761        /// * Collection Admin.762        /// 763        /// # Arguments764        /// 765        /// * collection_id: ID of the Collection to add admin for.766        /// 767        /// * new_admin_id: Address of new admin to add.768        #[weight = T::WeightInfo::add_collection_admin()]769        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {770771            let sender = ensure_signed(origin)?;772            Self::check_owner_or_admin_permissions(collection_id, sender)?;773            let mut admin_arr: Vec<T::AccountId> = Vec::new();774775            if <AdminList<T>>::contains_key(collection_id)776            {777                admin_arr = <AdminList<T>>::get(collection_id);778                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);779            }780781            // Number of collection admins782            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);783784            admin_arr.push(new_admin_id);785            <AdminList<T>>::insert(collection_id, admin_arr);786787            Ok(())788        }789790        /// 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.791        ///792        /// # Permissions793        /// 794        /// * Collection Owner.795        /// * Collection Admin.796        /// 797        /// # Arguments798        /// 799        /// * collection_id: ID of the Collection to remove admin for.800        /// 801        /// * account_id: Address of admin to remove.802        #[weight = T::WeightInfo::remove_collection_admin()]803        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {804805            let sender = ensure_signed(origin)?;806            Self::check_owner_or_admin_permissions(collection_id, sender)?;807808            if <AdminList<T>>::contains_key(collection_id)809            {810                let mut admin_arr = <AdminList<T>>::get(collection_id);811                admin_arr.retain(|i| *i != account_id);812                <AdminList<T>>::insert(collection_id, admin_arr);813            }814815            Ok(())816        }817818        /// # Permissions819        /// 820        /// * Collection Owner821        /// 822        /// # Arguments823        /// 824        /// * collection_id.825        /// 826        /// * new_sponsor.827        #[weight = T::WeightInfo::set_collection_sponsor()]828        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {829830            let sender = ensure_signed(origin)?;831            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);832833            let mut target_collection = <Collection<T>>::get(collection_id);834            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);835836            target_collection.sponsor = new_sponsor;837            target_collection.sponsor_confirmed = false;838            <Collection<T>>::insert(collection_id, target_collection);839840            Ok(())841        }842843        /// # Permissions844        /// 845        /// * Sponsor.846        /// 847        /// # Arguments848        /// 849        /// * collection_id.850        #[weight = T::WeightInfo::confirm_sponsorship()]851        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {852853            let sender = ensure_signed(origin)?;854            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);855856            let mut target_collection = <Collection<T>>::get(collection_id);857            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);858859            target_collection.sponsor_confirmed = true;860            <Collection<T>>::insert(collection_id, target_collection);861862            Ok(())863        }864865        /// Switch back to pay-per-own-transaction model.866        ///867        /// # Permissions868        ///869        /// * Collection owner.870        /// 871        /// # Arguments872        /// 873        /// * collection_id.874        #[weight = T::WeightInfo::remove_collection_sponsor()]875        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {876877            let sender = ensure_signed(origin)?;878            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);879880            let mut target_collection = <Collection<T>>::get(collection_id);881            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);882883            target_collection.sponsor = T::AccountId::default();884            target_collection.sponsor_confirmed = false;885            <Collection<T>>::insert(collection_id, target_collection);886887            Ok(())888        }889890        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.891        /// 892        /// # Permissions893        /// 894        /// * Collection Owner.895        /// * Collection Admin.896        /// * Anyone if897        ///     * White List is enabled, and898        ///     * Address is added to white list, and899        ///     * MintPermission is enabled (see SetMintPermission method)900        /// 901        /// # Arguments902        /// 903        /// * collection_id: ID of the collection.904        /// 905        /// * owner: Address, initial owner of the NFT.906        ///907        /// * data: Token data to store on chain.908        // #[weight =909        // (130_000_000 as Weight)910        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))911        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))912        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]913914        #[weight = T::WeightInfo::create_item(data.len())]915        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {916917            let sender = ensure_signed(origin)?;918919            Self::collection_exists(collection_id)?;920921            let target_collection = <Collection<T>>::get(collection_id);922923            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;924            Self::validate_create_item_args(&target_collection, &data)?;925            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;926927            Ok(())928        }929930        /// This method creates multiple instances of NFT Collection created with CreateCollection method.931        /// 932        /// # Permissions933        /// 934        /// * Collection Owner.935        /// * Collection Admin.936        /// * Anyone if937        ///     * White List is enabled, and938        ///     * Address is added to white list, and939        ///     * MintPermission is enabled (see SetMintPermission method)940        /// 941        /// # Arguments942        /// 943        /// * collection_id: ID of the collection.944        /// 945        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].946        /// 947        /// * owner: Address, initial owner of the NFT.948        #[weight = T::WeightInfo::create_item(items_data.into_iter()949                               .map(|data| { data.len() })950                               .sum())]951        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {952953            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);954            let sender = ensure_signed(origin)?;955956            Self::collection_exists(collection_id)?;957            let target_collection = <Collection<T>>::get(collection_id);958959            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;960961            for data in &items_data {962                Self::validate_create_item_args(&target_collection, data)?;963            }964            for data in &items_data {965                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;966            }967968            Ok(())969        }970971        /// Destroys a concrete instance of NFT.972        /// 973        /// # Permissions974        /// 975        /// * Collection Owner.976        /// * Collection Admin.977        /// * Current NFT Owner.978        /// 979        /// # Arguments980        /// 981        /// * collection_id: ID of the collection.982        /// 983        /// * item_id: ID of NFT to burn.984        #[weight = T::WeightInfo::burn_item()]985        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {986987            let sender = ensure_signed(origin)?;988            Self::collection_exists(collection_id)?;989990            // Transfer permissions check991            let target_collection = <Collection<T>>::get(collection_id);992            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||993                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),994                Error::<T>::NoPermission);995996            if target_collection.access == AccessMode::WhiteList {997                Self::check_white_list(collection_id, &sender)?;998            }9991000            match target_collection.mode1001            {1002                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1003                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1004                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1005                _ => ()1006            };10071008            // call event1009            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10101011            Ok(())1012        }10131014        /// Change ownership of the token.1015        /// 1016        /// # Permissions1017        /// 1018        /// * Collection Owner1019        /// * Collection Admin1020        /// * Current NFT owner1021        ///1022        /// # Arguments1023        /// 1024        /// * recipient: Address of token recipient.1025        /// 1026        /// * collection_id.1027        /// 1028        /// * item_id: ID of the item1029        ///     * Non-Fungible Mode: Required.1030        ///     * Fungible Mode: Ignored.1031        ///     * Re-Fungible Mode: Required.1032        /// 1033        /// * value: Amount to transfer.1034        ///     * Non-Fungible Mode: Ignored1035        ///     * Fungible Mode: Must specify transferred amount1036        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1037        #[weight = T::WeightInfo::transfer()]1038        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10391040            let sender = ensure_signed(origin)?;1041            let target_collection = <Collection<T>>::get(collection_id);10421043            // Limits check1044            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;10451046            // Transfer permissions check1047            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1048                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1049                Error::<T>::NoPermission);10501051            if target_collection.access == AccessMode::WhiteList {1052                Self::check_white_list(collection_id, &sender)?;1053                Self::check_white_list(collection_id, &recipient)?;1054            }10551056            match target_collection.mode1057            {1058                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1059                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1060                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1061                _ => ()1062            };10631064            Ok(())1065        }10661067        /// Set, change, or remove approved address to transfer the ownership of the NFT.1068        /// 1069        /// # Permissions1070        /// 1071        /// * Collection Owner1072        /// * Collection Admin1073        /// * Current NFT owner1074        /// 1075        /// # Arguments1076        /// 1077        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1078        /// 1079        /// * collection_id.1080        /// 1081        /// * item_id: ID of the item.1082        #[weight = T::WeightInfo::approve()]1083        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {10841085            let sender = ensure_signed(origin)?;10861087            // Transfer permissions check1088            let target_collection = <Collection<T>>::get(collection_id);1089            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1090                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1091                Error::<T>::NoPermission);10921093            if target_collection.access == AccessMode::WhiteList {1094                Self::check_white_list(collection_id, &sender)?;1095                Self::check_white_list(collection_id, &spender)?;1096            }10971098            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1099            let mut allowance: u128 = amount;1100            if allowance_exists {1101                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1102            }1103            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11041105            Ok(())1106        }1107        1108        /// 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.1109        /// 1110        /// # Permissions1111        /// * Collection Owner1112        /// * Collection Admin1113        /// * Current NFT owner1114        /// * Address approved by current NFT owner1115        /// 1116        /// # Arguments1117        /// 1118        /// * from: Address that owns token.1119        /// 1120        /// * recipient: Address of token recipient.1121        /// 1122        /// * collection_id.1123        /// 1124        /// * item_id: ID of the item.1125        /// 1126        /// * value: Amount to transfer.1127        #[weight = T::WeightInfo::transfer_from()]1128        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11291130            let sender = ensure_signed(origin)?;1131            let mut appoved_transfer = false;11321133            // Check approval1134            let mut approval: u128 = 0;1135            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &recipient)) {1136                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &recipient));1137                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1138                appoved_transfer = true;1139            }11401141            let target_collection = <Collection<T>>::get(collection_id);11421143            // Limits check1144            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11451146            // Transfer permissions check         1147            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1148                Error::<T>::NoPermission);11491150            if target_collection.access == AccessMode::WhiteList {1151                Self::check_white_list(collection_id, &sender)?;1152                Self::check_white_list(collection_id, &recipient)?;1153            }11541155            // Reduce approval by transferred amount or remove if remaining approval drops to 01156            if approval.checked_sub(value).unwrap_or(0) > 0 {1157                <Allowances<T>>::insert(collection_id, (item_id, &from, &recipient), approval - value);1158            }1159            else {1160                <Allowances<T>>::remove(collection_id, (item_id, &from, &recipient));1161            }11621163            match target_collection.mode1164            {1165                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1166                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1167                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1168                _ => ()1169            };11701171            Ok(())1172        }11731174        #[weight = 0]1175        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11761177            // let no_perm_mes = "You do not have permissions to modify this collection";1178            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1179            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1180            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11811182            // // on_nft_received  call11831184            // Self::transfer(origin, collection_id, item_id, new_owner)?;11851186            Ok(())1187        }11881189        /// Set off-chain data schema.1190        /// 1191        /// # Permissions1192        /// 1193        /// * Collection Owner1194        /// * Collection Admin1195        /// 1196        /// # Arguments1197        /// 1198        /// * collection_id.1199        /// 1200        /// * schema: String representing the offchain data schema.1201        #[weight = T::WeightInfo::set_variable_meta_data()]1202        pub fn set_variable_meta_data (1203            origin,1204            collection_id: CollectionId,1205            item_id: TokenId,1206            data: Vec<u8>1207        ) -> DispatchResult {1208            let sender = ensure_signed(origin)?;1209            1210            Self::collection_exists(collection_id)?;1211            1212            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12131214            // Modify permissions check1215            let target_collection = <Collection<T>>::get(collection_id);1216            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1217                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1218                Error::<T>::NoPermission);12191220            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12211222            match target_collection.mode1223            {1224                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1225                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1226                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1227                _ => fail!(Error::<T>::UnexpectedCollectionType)1228            };12291230            Ok(())1231        }1232 1233        /// Set schema standard1234        /// ImageURL1235        /// Unique1236        /// 1237        /// # Permissions1238        /// 1239        /// * Collection Owner1240        /// * Collection Admin1241        /// 1242        /// # Arguments1243        /// 1244        /// * collection_id.1245        /// 1246        /// * schema: SchemaVersion: enum1247        #[weight = T::WeightInfo::set_schema_version()]1248        pub fn set_schema_version(1249            origin,1250            collection_id: CollectionId,1251            version: SchemaVersion1252        ) -> DispatchResult {1253            let sender = ensure_signed(origin)?;1254            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1255            let mut target_collection = <Collection<T>>::get(collection_id);1256            target_collection.schema_version = version;1257            <Collection<T>>::insert(collection_id, target_collection);12581259            Ok(())1260        }12611262        /// Set off-chain data schema.1263        /// 1264        /// # Permissions1265        /// 1266        /// * Collection Owner1267        /// * Collection Admin1268        /// 1269        /// # Arguments1270        /// 1271        /// * collection_id.1272        /// 1273        /// * schema: String representing the offchain data schema.1274        #[weight = T::WeightInfo::set_offchain_schema()]1275        pub fn set_offchain_schema(1276            origin,1277            collection_id: CollectionId,1278            schema: Vec<u8>1279        ) -> DispatchResult {1280            let sender = ensure_signed(origin)?;1281            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12821283            let mut target_collection = <Collection<T>>::get(collection_id);1284            target_collection.offchain_schema = schema;1285            <Collection<T>>::insert(collection_id, target_collection);12861287            Ok(())1288        }12891290        /// Set const on-chain data schema.1291        /// 1292        /// # Permissions1293        /// 1294        /// * Collection Owner1295        /// * Collection Admin1296        /// 1297        /// # Arguments1298        /// 1299        /// * collection_id.1300        /// 1301        /// * schema: String representing the const on-chain data schema.1302        #[weight = T::WeightInfo::set_const_on_chain_schema()]1303        pub fn set_const_on_chain_schema (1304            origin,1305            collection_id: CollectionId,1306            schema: Vec<u8>1307        ) -> DispatchResult {1308            let sender = ensure_signed(origin)?;1309            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13101311            let mut target_collection = <Collection<T>>::get(collection_id);1312            target_collection.const_on_chain_schema = schema;1313            <Collection<T>>::insert(collection_id, target_collection);13141315            Ok(())1316        }13171318        /// Set variable on-chain data schema.1319        /// 1320        /// # Permissions1321        /// 1322        /// * Collection Owner1323        /// * Collection Admin1324        /// 1325        /// # Arguments1326        /// 1327        /// * collection_id.1328        /// 1329        /// * schema: String representing the variable on-chain data schema.1330        #[weight = T::WeightInfo::set_const_on_chain_schema()]1331        pub fn set_variable_on_chain_schema (1332            origin,1333            collection_id: CollectionId,1334            schema: Vec<u8>1335        ) -> DispatchResult {1336            let sender = ensure_signed(origin)?;1337            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13381339            let mut target_collection = <Collection<T>>::get(collection_id);1340            target_collection.variable_on_chain_schema = schema;1341            <Collection<T>>::insert(collection_id, target_collection);13421343            Ok(())1344        }13451346        // Sudo permissions function1347        #[weight = T::WeightInfo::set_chain_limits()]1348        pub fn set_chain_limits(1349            origin,1350            limits: ChainLimits1351        ) -> DispatchResult {13521353            #[cfg(not(feature = "runtime-benchmarks"))]1354            ensure_root(origin)?;13551356            <ChainLimit>::put(limits);1357            Ok(())1358        }13591360        /// Enable smart contract self-sponsoring.1361        /// 1362        /// # Permissions1363        /// 1364        /// * Contract Owner1365        /// 1366        /// # Arguments1367        /// 1368        /// * contract address1369        /// * enable flag1370        /// 1371        #[weight = T::WeightInfo::enable_contract_sponsoring()]1372        pub fn enable_contract_sponsoring(1373            origin,1374            contract_address: T::AccountId,1375            enable: bool1376        ) -> DispatchResult {13771378            let sender = ensure_signed(origin)?;13791380            #[cfg(feature = "runtime-benchmarks")]1381            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13821383            Self::ensure_contract_owned(sender, &contract_address)?;13841385            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1386            Ok(())1387        }13881389        /// Set the rate limit for contract sponsoring to specified number of blocks.1390        /// 1391        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1392        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1393        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1394        /// from contract endowment if there are at least B blocks between such transactions. 1395        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1396        /// 1397        /// # Permissions1398        /// 1399        /// * Contract Owner1400        /// 1401        /// # Arguments1402        /// 1403        /// -`contract_address`: Address of the contract to sponsor1404        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1405        /// 1406        #[weight = T::WeightInfo::set_contract_sponsoring_rate_limit()]1407        pub fn set_contract_sponsoring_rate_limit(1408            origin,1409            contract_address: T::AccountId,1410            rate_limit: T::BlockNumber1411        ) -> DispatchResult {1412            let sender = ensure_signed(origin)?;14131414            #[cfg(feature = "runtime-benchmarks")]1415            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14161417            Self::ensure_contract_owned(sender, &contract_address)?;1418            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1419            Ok(())1420        }14211422        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1423        /// 1424        /// # Permissions1425        /// 1426        /// * Address that deployed smart contract.1427        /// 1428        /// # Arguments1429        /// 1430        /// -`contract_address`: Address of the contract.1431        /// 1432        /// - `enable`: .  1433        #[weight = T::WeightInfo::toggle_contract_white_list()]1434        pub fn toggle_contract_white_list(1435            origin,1436            contract_address: T::AccountId,1437            enable: bool1438        ) -> DispatchResult {1439            let sender = ensure_signed(origin)?;14401441            #[cfg(feature = "runtime-benchmarks")]1442            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14431444            Self::ensure_contract_owned(sender, &contract_address)?;1445            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1446            Ok(())1447        }1448        1449        /// Add an address to smart contract white list.1450        /// 1451        /// # Permissions1452        /// 1453        /// * Address that deployed smart contract.1454        /// 1455        /// # Arguments1456        /// 1457        /// -`contract_address`: Address of the contract.1458        ///1459        /// -`account_address`: Address to add.1460        #[weight = T::WeightInfo::add_to_contract_white_list()]1461        pub fn add_to_contract_white_list(1462            origin,1463            contract_address: T::AccountId,1464            account_address: T::AccountId1465        ) -> DispatchResult {1466            let sender = ensure_signed(origin)?;14671468            #[cfg(feature = "runtime-benchmarks")]1469            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1470            1471            Self::ensure_contract_owned(sender, &contract_address)?;      1472            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1473            Ok(())1474        }14751476        /// Remove an address from smart contract white list.1477        /// 1478        /// # Permissions1479        /// 1480        /// * Address that deployed smart contract.1481        /// 1482        /// # Arguments1483        /// 1484        /// -`contract_address`: Address of the contract.1485        ///1486        /// -`account_address`: Address to remove.1487        #[weight = T::WeightInfo::remove_from_contract_white_list()]1488        pub fn remove_from_contract_white_list(1489            origin,1490            contract_address: T::AccountId,1491            account_address: T::AccountId1492        ) -> DispatchResult {1493            let sender = ensure_signed(origin)?;14941495            #[cfg(feature = "runtime-benchmarks")]1496            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14971498            Self::ensure_contract_owned(sender, &contract_address)?;1499            <ContractWhiteList<T>>::remove(contract_address, account_address);1500            Ok(())1501        }15021503        #[weight = T::WeightInfo::set_collection_limits()]1504        pub fn set_collection_limits(1505            origin,1506            collection_id: u32,1507            limits: CollectionLimits,1508        ) -> DispatchResult {1509            let sender = ensure_signed(origin)?;1510            Self::check_owner_permissions(collection_id, sender.clone())?;1511            let mut target_collection = <Collection<T>>::get(collection_id);1512            let chain_limits = ChainLimit::get();1513            let climits = target_collection.limits;15141515            // collection bounds1516            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1517                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1518                Error::<T>::CollectionLimitBoundsExceeded);15191520            // token_limit   check  prev1521            ensure!(climits.token_limit > limits.token_limit && 1522                limits.token_limit <= chain_limits.account_token_ownership_limit, 1523                Error::<T>::AccountTokenLimitExceeded);15241525            target_collection.limits = limits;1526            <Collection<T>>::insert(collection_id, target_collection);15271528            Ok(())1529        } 1530    }1531}15321533impl<T: Trait> Module<T> {15341535    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15361537        // check token limit and account token limit1538        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1539        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1540        1541        Ok(())1542    }15431544    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15451546        // check token limit and account token limit1547        let total_items: u32 = ItemListIndex::get(collection_id);1548        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1549        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1550        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15511552        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1553            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1554            Self::check_white_list(collection_id, owner)?;1555            Self::check_white_list(collection_id, sender)?;1556        }15571558        Ok(())1559    }15601561    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1562        match target_collection.mode1563        {1564            CollectionMode::NFT => {1565                if let CreateItemData::NFT(data) = data {1566                    // check sizes1567                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1568                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1569                } else {1570                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1571                }1572            },1573            CollectionMode::Fungible(_) => {1574                if let CreateItemData::Fungible(_) = data {1575                } else {1576                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1577                }1578            },1579            CollectionMode::ReFungible(_) => {1580                if let CreateItemData::ReFungible(data) = data {15811582                    // check sizes1583                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1584                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1585                } else {1586                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1587                }1588            },1589            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1590        };15911592        Ok(())1593    }15941595    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1596        match data1597        {1598            CreateItemData::NFT(data) => {1599                let item = NftItemType {1600                    owner,1601                    const_data: data.const_data,1602                    variable_data: data.variable_data1603                };16041605                Self::add_nft_item(collection_id, item)?;1606            },1607            CreateItemData::Fungible(data) => {1608                Self::add_fungible_item(collection_id, &owner, data.value)?;1609            },1610            CreateItemData::ReFungible(data) => {1611                let mut owner_list = Vec::new();1612                let value = (10 as u128).pow(collection.decimal_points as u32);1613                owner_list.push(Ownership {owner: owner.clone(), fraction: value});16141615                let item = ReFungibleItemType {1616                    owner: owner_list,1617                    const_data: data.const_data,1618                    variable_data: data.variable_data1619                };16201621                Self::add_refungible_item(collection_id, item)?;1622            }1623        };16241625        // call event1626        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16271628        Ok(())1629    }16301631    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {16321633        // Does new owner already have an account?1634        let mut balance: u128 = 0;1635        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1636            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1637        } 16381639        // Mint 1640        let item = FungibleItemType {1641            value: balance + value1642        };1643        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16441645        // Update balance1646        let new_balance = <Balance<T>>::get(collection_id, owner)1647            .checked_add(value)1648            .ok_or(Error::<T>::NumOverflow)?;1649        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);16501651        Ok(())1652    }16531654    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1655        let current_index = <ItemListIndex>::get(collection_id)1656            .checked_add(1)1657            .ok_or(Error::<T>::NumOverflow)?;1658        let itemcopy = item.clone();16591660        let value = item.owner.first().unwrap().fraction;1661        let owner = item.owner.first().unwrap().owner.clone();16621663        Self::add_token_index(collection_id, current_index, owner.clone())?;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.clone())1670            .checked_add(value)1671            .ok_or(Error::<T>::NumOverflow)?;1672        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);16731674        Ok(())1675    }16761677    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1678        let current_index = <ItemListIndex>::get(collection_id)1679            .checked_add(1)1680            .ok_or(Error::<T>::NumOverflow)?;16811682        let item_owner = item.owner.clone();1683        Self::add_token_index(collection_id, current_index, item.owner.clone())?;16841685        <ItemListIndex>::insert(collection_id, current_index);1686        <NftItemList<T>>::insert(collection_id, current_index, item);16871688        // Update balance1689        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1690            .checked_add(1)1691            .ok_or(Error::<T>::NumOverflow)?;1692        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);16931694        Ok(())1695    }16961697    fn burn_refungible_item(1698        collection_id: CollectionId,1699        item_id: TokenId,1700        owner: T::AccountId,1701    ) -> DispatchResult {1702        ensure!(1703            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1704            Error::<T>::TokenNotFound1705        );1706        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1707        let item = collection1708            .owner1709            .iter()1710            .filter(|&i| i.owner == owner)1711            .next()1712            .unwrap();1713        Self::remove_token_index(collection_id, item_id, owner.clone())?;17141715        // update balance1716        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1717            .checked_sub(item.fraction)1718            .ok_or(Error::<T>::NumOverflow)?;1719        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17201721        <ReFungibleItemList<T>>::remove(collection_id, item_id);17221723        Ok(())1724    }17251726    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1727        ensure!(1728            <NftItemList<T>>::contains_key(collection_id, item_id),1729            Error::<T>::TokenNotFound1730        );1731        let item = <NftItemList<T>>::get(collection_id, item_id);1732        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17331734        // update balance1735        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1736            .checked_sub(1)1737            .ok_or(Error::<T>::NumOverflow)?;1738        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1739        <NftItemList<T>>::remove(collection_id, item_id);17401741        Ok(())1742    }17431744    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1745        ensure!(1746            <FungibleItemList<T>>::contains_key(collection_id, owner),1747            Error::<T>::TokenNotFound1748        );1749        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1750        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);17511752        // update balance1753        let new_balance = <Balance<T>>::get(collection_id, owner)1754            .checked_sub(value)1755            .ok_or(Error::<T>::NumOverflow)?;1756        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17571758        if balance.value - value > 0 {1759            balance.value -= value;1760            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1761        }1762        else {1763            <FungibleItemList<T>>::remove(collection_id, owner);1764        }17651766        Ok(())1767    }17681769    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1770        ensure!(1771            <Collection<T>>::contains_key(collection_id),1772            Error::<T>::CollectionNotFound1773        );1774        Ok(())1775    }17761777    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1778        Self::collection_exists(collection_id)?;17791780        let target_collection = <Collection<T>>::get(collection_id);1781        ensure!(1782            subject == target_collection.owner,1783            Error::<T>::NoPermission1784        );17851786        Ok(())1787    }17881789    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1790        let target_collection = <Collection<T>>::get(collection_id);1791        let mut result: bool = subject == target_collection.owner;1792        let exists = <AdminList<T>>::contains_key(collection_id);17931794        if !result & exists {1795            if <AdminList<T>>::get(collection_id).contains(&subject) {1796                result = true1797            }1798        }17991800        result1801    }18021803    fn check_owner_or_admin_permissions(1804        collection_id: CollectionId,1805        subject: T::AccountId,1806    ) -> DispatchResult {1807        Self::collection_exists(collection_id)?;1808        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18091810        ensure!(1811            result,1812            Error::<T>::NoPermission1813        );1814        Ok(())1815    }18161817    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1818        let target_collection = <Collection<T>>::get(collection_id);18191820        match target_collection.mode {1821            CollectionMode::NFT => {1822                <NftItemList<T>>::get(collection_id, item_id).owner == subject1823            }1824            CollectionMode::Fungible(_) => {1825                <FungibleItemList<T>>::contains_key(collection_id, &subject)1826            }1827            CollectionMode::ReFungible(_) => {1828                <ReFungibleItemList<T>>::get(collection_id, item_id)1829                    .owner1830                    .iter()1831                    .any(|i| i.owner == subject)1832            }1833            CollectionMode::Invalid => false,1834        }1835    }18361837    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1838        let mes = Error::<T>::AddresNotInWhiteList;1839        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);18401841        Ok(())1842    }18431844    fn transfer_fungible(1845        collection_id: CollectionId,1846        value: u128,1847        owner: &T::AccountId,1848        recipient: &T::AccountId,1849    ) -> DispatchResult {1850        ensure!(1851            <FungibleItemList<T>>::contains_key(collection_id, owner),1852            Error::<T>::TokenNotFound1853        );18541855        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1856        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);18571858        // Send balance to recipient (updates balanceOf of recipient)1859        Self::add_fungible_item(collection_id, recipient, value)?;18601861        // update balanceOf of sender1862        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);18631864        // Reduce or remove sender1865        if balance.value == value {1866            <FungibleItemList<T>>::remove(collection_id, owner);1867        }1868        else {1869            balance.value -= value;1870            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1871        }18721873        Ok(())1874    }18751876    fn transfer_refungible(1877        collection_id: CollectionId,1878        item_id: TokenId,1879        value: u128,1880        owner: T::AccountId,1881        new_owner: T::AccountId,1882    ) -> DispatchResult {1883        ensure!(1884            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1885            Error::<T>::TokenNotFound1886        );18871888        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1889        let item = full_item1890            .owner1891            .iter()1892            .filter(|i| i.owner == owner)1893            .next()1894            .ok_or(Error::<T>::NumOverflow)?;1895        let amount = item.fraction;18961897        ensure!(amount >= value, Error::<T>::TokenValueTooLow);18981899        // update balance1900        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1901            .checked_sub(value)1902            .ok_or(Error::<T>::NumOverflow)?;1903        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19041905        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1906            .checked_add(value)1907            .ok_or(Error::<T>::NumOverflow)?;1908        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19091910        let old_owner = item.owner.clone();1911        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19121913        // transfer1914        if amount == value && !new_owner_has_account {1915            // change owner1916            // new owner do not have account1917            let mut new_full_item = full_item.clone();1918            new_full_item1919                .owner1920                .iter_mut()1921                .find(|i| i.owner == owner)1922                .unwrap()1923                .owner = new_owner.clone();1924            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19251926            // update index collection1927            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1928        } else {1929            let mut new_full_item = full_item.clone();1930            new_full_item1931                .owner1932                .iter_mut()1933                .find(|i| i.owner == owner)1934                .unwrap()1935                .fraction -= value;19361937            // separate amount1938            if new_owner_has_account {1939                // new owner has account1940                new_full_item1941                    .owner1942                    .iter_mut()1943                    .find(|i| i.owner == new_owner)1944                    .unwrap()1945                    .fraction += value;1946            } else {1947                // new owner do not have account1948                new_full_item.owner.push(Ownership {1949                    owner: new_owner.clone(),1950                    fraction: value,1951                });1952                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1953            }19541955            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1956        }19571958        Ok(())1959    }19601961    fn transfer_nft(1962        collection_id: CollectionId,1963        item_id: TokenId,1964        sender: T::AccountId,1965        new_owner: T::AccountId,1966    ) -> DispatchResult {1967        ensure!(1968            <NftItemList<T>>::contains_key(collection_id, item_id),1969            Error::<T>::TokenNotFound1970        );19711972        let mut item = <NftItemList<T>>::get(collection_id, item_id);19731974        ensure!(1975            sender == item.owner,1976            Error::<T>::MustBeTokenOwner1977        );19781979        // update balance1980        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1981            .checked_sub(1)1982            .ok_or(Error::<T>::NumOverflow)?;1983        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19841985        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1986            .checked_add(1)1987            .ok_or(Error::<T>::NumOverflow)?;1988        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19891990        // change owner1991        let old_owner = item.owner.clone();1992        item.owner = new_owner.clone();1993        <NftItemList<T>>::insert(collection_id, item_id, item);19941995        // update index collection1996        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;19971998        Ok(())1999    }2000    2001    fn item_exists(2002        collection_id: CollectionId,2003        item_id: TokenId,2004        mode: &CollectionMode2005    ) -> DispatchResult {2006        match mode {2007            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2008            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2009            _ => ()2010        };2011        2012        Ok(())2013    }20142015    fn set_re_fungible_variable_data(2016        collection_id: CollectionId,2017        item_id: TokenId,2018        data: Vec<u8>2019    ) -> DispatchResult {2020        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20212022        item.variable_data = data;20232024        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20252026        Ok(())2027    }20282029    fn set_nft_variable_data(2030        collection_id: CollectionId,2031        item_id: TokenId,2032        data: Vec<u8>2033    ) -> DispatchResult {2034        let mut item = <NftItemList<T>>::get(collection_id, item_id);2035        2036        item.variable_data = data;20372038        <NftItemList<T>>::insert(collection_id, item_id, item);2039        2040        Ok(())2041    }20422043    fn init_collection(item: &CollectionType<T::AccountId>) {2044        // check params2045        assert!(2046            item.decimal_points <= MAX_DECIMAL_POINTS,2047            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2048        );2049        assert!(2050            item.name.len() <= 64,2051            "Collection name can not be longer than 63 char"2052        );2053        assert!(2054            item.name.len() <= 256,2055            "Collection description can not be longer than 255 char"2056        );2057        assert!(2058            item.token_prefix.len() <= 16,2059            "Token prefix can not be longer than 15 char"2060        );20612062        // Generate next collection ID2063        let next_id = CreatedCollectionCount::get()2064            .checked_add(1)2065            .unwrap();20662067        CreatedCollectionCount::put(next_id);2068    }20692070    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2071        let current_index = <ItemListIndex>::get(collection_id)2072            .checked_add(1)2073            .unwrap();20742075        let item_owner = item.owner.clone();2076        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();20772078        <ItemListIndex>::insert(collection_id, current_index);20792080        // Update balance2081        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2082            .checked_add(1)2083            .unwrap();2084        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2085    }20862087    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2088        let current_index = <ItemListIndex>::get(collection_id)2089            .checked_add(1)2090            .unwrap();20912092        Self::add_token_index(collection_id, current_index, (*owner).clone()).unwrap();20932094        <ItemListIndex>::insert(collection_id, current_index);20952096        // Update balance2097        let new_balance = <Balance<T>>::get(collection_id, owner)2098            .checked_add(item.value)2099            .unwrap();2100        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2101    }21022103    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2104        let current_index = <ItemListIndex>::get(collection_id)2105            .checked_add(1)2106            .unwrap();21072108        let value = item.owner.first().unwrap().fraction;2109        let owner = item.owner.first().unwrap().owner.clone();21102111        Self::add_token_index(collection_id, current_index, owner.clone()).unwrap();21122113        <ItemListIndex>::insert(collection_id, current_index);21142115        // Update balance2116        let new_balance = <Balance<T>>::get(collection_id, owner.clone())2117            .checked_add(value)2118            .unwrap();2119        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2120    }21212122    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {21232124        // add to account limit2125        if <AccountItemCount<T>>::contains_key(owner.clone()) {21262127            // bound Owned tokens by a single address2128            let count = <AccountItemCount<T>>::get(owner.clone());2129            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21302131            <AccountItemCount<T>>::insert(owner.clone(), count2132                .checked_add(1)2133                .ok_or(Error::<T>::NumOverflow)?);2134        }2135        else {2136            <AccountItemCount<T>>::insert(owner.clone(), 1);2137        }21382139        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2140        if list_exists {2141            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2142            let item_contains = list.contains(&item_index.clone());21432144            if !item_contains {2145                list.push(item_index.clone());2146            }21472148            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2149        } else {2150            let mut itm = Vec::new();2151            itm.push(item_index.clone());2152            <AddressTokens<T>>::insert(collection_id, owner, itm);2153            2154        }21552156        Ok(())2157    }21582159    fn remove_token_index(2160        collection_id: CollectionId,2161        item_index: TokenId,2162        owner: T::AccountId,2163    ) -> DispatchResult {21642165        // update counter2166        <AccountItemCount<T>>::insert(owner.clone(), 2167            <AccountItemCount<T>>::get(owner.clone())2168            .checked_sub(1)2169            .ok_or(Error::<T>::NumOverflow)?);217021712172        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2173        if list_exists {2174            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2175            let item_contains = list.contains(&item_index.clone());21762177            if item_contains {2178                list.retain(|&item| item != item_index);2179                <AddressTokens<T>>::insert(collection_id, owner, list);2180            }2181        }21822183        Ok(())2184    }21852186    fn move_token_index(2187        collection_id: CollectionId,2188        item_index: TokenId,2189        old_owner: T::AccountId,2190        new_owner: T::AccountId,2191    ) -> DispatchResult {2192        Self::remove_token_index(collection_id, item_index, old_owner)?;2193        Self::add_token_index(collection_id, item_index, new_owner)?;21942195        Ok(())2196    }2197    2198    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2199        if <ContractOwner<T>>::contains_key(contract.clone()) {2200            let owner = <ContractOwner<T>>::get(contract);2201            ensure!(account == owner, Error::<T>::NoPermission);2202        } else {2203            fail!(Error::<T>::NoPermission);2204        }22052206        Ok(())2207    }2208}22092210////////////////////////////////////////////////////////////////////////////////////////////////////2211// Economic models2212// #region22132214/// Fee multiplier.2215pub type Multiplier = FixedU128;22162217type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2218    <T as system::Trait>::AccountId,2219>>::Balance;2220type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2221    <T as system::Trait>::AccountId,2222>>::NegativeImbalance;22232224/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2225/// in the queue.2226#[derive(Encode, Decode, Clone, Eq, PartialEq)]2227pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2228    #[codec(compact)] BalanceOf<T>2229);22302231impl<T: Trait + Send + Sync> sp_std::fmt::Debug2232    for ChargeTransactionPayment<T>2233{2234    #[cfg(feature = "std")]2235    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2236        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2237    }2238    #[cfg(not(feature = "std"))]2239    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2240        Ok(())2241    }2242}22432244impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2245where2246    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2247    BalanceOf<T>: Send + Sync + FixedPointOperand,2248{2249    /// utility constructor. Used only in client/factory code.2250    pub fn from(fee: BalanceOf<T>) -> Self {2251        Self(fee)2252    }22532254    pub fn traditional_fee(2255        len: usize,2256        info: &DispatchInfoOf<T::Call>,2257        tip: BalanceOf<T>,2258    ) -> BalanceOf<T>2259    where2260        T::Call: Dispatchable<Info = DispatchInfo>,2261    {2262        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2263    }22642265	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2266		let weight_saturation = T::MaximumBlockWeight::get() / info.weight.max(1);2267		let len_saturation = T::MaximumBlockLength::get() as u64 / (len as u64).max(1);2268		let coefficient: BalanceOf<T> = weight_saturation.min(len_saturation).saturated_into::<BalanceOf<T>>();2269		final_fee.saturating_mul(coefficient).saturated_into::<TransactionPriority>()2270	}22712272    fn withdraw_fee(2273        &self,2274        who: &T::AccountId,2275        call: &T::Call,2276        info: &DispatchInfoOf<T::Call>,2277        len: usize,2278    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2279        let tip = self.0;22802281        // Set fee based on call type. Creating collection costs 1 Unique.2282        // All other transactions have traditional fees so far2283        // let fee = match call.is_sub_type() {2284        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2285        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2286        //                                                 // _ => <BalanceOf<T>>::from(100)2287        // };2288        let fee = Self::traditional_fee(len, info, tip);22892290        // Only mess with balances if fee is not zero.2291        if fee.is_zero() {2292            return Ok((fee, None));2293        }22942295        // Determine who is paying transaction fee based on ecnomic model2296        // Parse call to extract collection ID and access collection sponsor2297        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2298            Some(Call::create_item(collection_id, _owner, _properties)) => {22992300                // check free create limit2301                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2302                   (<Collection<T>>::get(collection_id).sponsor_confirmed)2303                {2304                    <Collection<T>>::get(collection_id).sponsor2305                } else {2306                    T::AccountId::default()2307                }2308            }2309            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2310                2311                let mut sponsor_transfer = false;2312                if <Collection<T>>::get(collection_id).sponsor_confirmed {23132314                    let collection_limits = <Collection<T>>::get(collection_id).limits;2315                    let collection_mode = <Collection<T>>::get(collection_id).mode;2316    2317                    // sponsor timeout2318                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2319                    sponsor_transfer = match collection_mode {2320                        CollectionMode::NFT => {2321    2322                            // get correct limit2323                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2324                                collection_limits.sponsor_transfer_timeout2325                            } else {2326                                ChainLimit::get().nft_sponsor_transfer_timeout2327                            };2328    2329                            let mut sponsored = true;2330                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2331                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2332                                let limit_time = last_tx_block + limit.into();2333                                if block_number <= limit_time {2334                                    sponsored = false;2335                                }2336                            }2337                            if sponsored {2338                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2339                            }23402341                            sponsored2342                        }2343                        CollectionMode::Fungible(_) => {2344    2345                            // get correct limit2346                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2347                                collection_limits.sponsor_transfer_timeout2348                            } else {2349                                ChainLimit::get().fungible_sponsor_transfer_timeout2350                            };2351    2352                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2353                            let mut sponsored = true;2354                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2355                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2356                                let limit_time = last_tx_block + limit.into();2357                                if block_number <= limit_time {2358                                    sponsored = false;2359                                }2360                            }2361                            if sponsored {2362                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2363                            }23642365                            sponsored2366                        }2367                        CollectionMode::ReFungible(_) => {2368    2369                            // get correct limit2370                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2371                                collection_limits.sponsor_transfer_timeout2372                            } else {2373                                ChainLimit::get().refungible_sponsor_transfer_timeout2374                            };2375    2376                            let mut sponsored = true;2377                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2378                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2379                                let limit_time = last_tx_block + limit.into();2380                                if block_number <= limit_time {2381                                    sponsored = false;2382                                }2383                            }2384                            if sponsored {2385                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2386                            }23872388                            sponsored2389                        }2390                        _ => {2391                            false2392                        },2393                    };2394                }23952396                if !sponsor_transfer {2397                    T::AccountId::default()2398                } else {2399                    <Collection<T>>::get(collection_id).sponsor2400                }2401            }24022403            _ => T::AccountId::default(),2404        };24052406        // Sponsor smart contracts2407        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {24082409            // On instantiation: set the contract owner2410            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {24112412                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2413                    code_hash,2414                    &data,2415                    &who,2416                );2417                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());24182419                T::AccountId::default()2420            },24212422            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2423            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {24242425                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());24262427                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2428                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2429                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2430                  2431                if !owned_contract && white_list_enabled {2432                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2433                        return Err(InvalidTransaction::Call.into());2434                    }2435                }24362437                let mut sponsor_transfer = false;2438                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2439                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2440                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2441                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2442                    let limit_time = last_tx_block + rate_limit;24432444                    if block_number >= limit_time {2445                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2446                        sponsor_transfer = true;2447                    }2448                } else {2449                    sponsor_transfer = false;2450                }2451               2452                2453                let mut sp = T::AccountId::default();2454                if sponsor_transfer {2455                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2456                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2457                            sp = called_contract;2458                        }2459                    }2460                }24612462                sp2463            },24642465            _ => sponsor,2466        };24672468        let mut who_pays_fee: T::AccountId = sponsor.clone();2469        if sponsor == T::AccountId::default() {2470            who_pays_fee = who.clone();2471        }24722473        match <T as transaction_payment::Trait>::Currency::withdraw(2474            &who_pays_fee,2475            fee,2476            if tip.is_zero() {2477                WithdrawReason::TransactionPayment.into()2478            } else {2479                WithdrawReason::TransactionPayment | WithdrawReason::Tip2480            },2481            ExistenceRequirement::KeepAlive,2482        ) {2483            Ok(imbalance) => Ok((fee, Some(imbalance))),2484            Err(_) => Err(InvalidTransaction::Payment.into()),2485        }2486    }2487}248824892490impl<T: Trait + Send + Sync> SignedExtension2491    for ChargeTransactionPayment<T>2492where2493    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2494    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2495{2496    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2497    type AccountId = T::AccountId;2498    type Call = T::Call;2499    type AdditionalSigned = ();2500    type Pre = (2501        BalanceOf<T>,2502        Self::AccountId,2503        Option<NegativeImbalanceOf<T>>,2504        BalanceOf<T>,2505    );2506    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2507        Ok(())2508    }25092510    fn validate(2511        &self,2512        who: &Self::AccountId,2513        call: &Self::Call,2514        info: &DispatchInfoOf<Self::Call>,2515        len: usize,2516    ) -> TransactionValidity {2517		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2518		Ok(ValidTransaction {2519			priority: Self::get_priority(len, info, fee),2520			..Default::default()2521		})2522    }25232524    fn pre_dispatch(2525        self,2526        who: &Self::AccountId,2527        call: &Self::Call,2528        info: &DispatchInfoOf<Self::Call>,2529        len: usize,2530    ) -> Result<Self::Pre, TransactionValidityError> {2531        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2532        Ok((self.0, who.clone(), imbalance, fee))2533    }25342535    fn post_dispatch(2536        pre: Self::Pre,2537        info: &DispatchInfoOf<Self::Call>,2538        post_info: &PostDispatchInfoOf<Self::Call>,2539        len: usize,2540        _result: &DispatchResult,2541    ) -> Result<(), TransactionValidityError> {2542        let (tip, who, imbalance, fee) = pre;2543        if let Some(payed) = imbalance {2544            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2545                len as u32, info, post_info, tip,2546            );2547            let refund = fee.saturating_sub(actual_fee);2548            let actual_payment =2549                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2550                    &who, refund,2551                ) {2552                    Ok(refund_imbalance) => {2553                        // The refund cannot be larger than the up front payed max weight.2554                        // `PostDispatchInfo::calc_unspent` guards against such a case.2555                        match payed.offset(refund_imbalance) {2556                            Ok(actual_payment) => actual_payment,2557                            Err(_) => return Err(InvalidTransaction::Payment.into()),2558                        }2559                    }2560                    // We do not recreate the account using the refund. The up front payment2561                    // is gone in that case.2562                    Err(_) => payed,2563                };2564            let imbalances = actual_payment.split(tip);2565            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2566                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2567            );2568        }2569        Ok(())2570    }2571}25722573// #endregion
modifiedpallets/nft/src/tests.rsdiffbeforeafterboth
--- a/pallets/nft/src/tests.rs
+++ b/pallets/nft/src/tests.rs
@@ -1,7 +1,7 @@
 // Tests to be written here
 use super::*;
 use crate::mock::*;
-use crate::{AccessMode, ApprovePermissions, CollectionMode,
+use crate::{AccessMode, CollectionMode,
     Ownership, ChainLimits, CreateItemData, CreateNftData, CreateFungibleData, CreateReFungibleData,
     CollectionId, TokenId, MAX_DECIMAL_POINTS};
 use frame_support::{assert_noop, assert_ok};
@@ -28,7 +28,7 @@
 }
 
 fn default_fungible_data () -> CreateFungibleData {
-    CreateFungibleData { }
+    CreateFungibleData { value: 5 }
 }
 
 fn default_re_fungible_data () -> CreateReFungibleData {
@@ -238,35 +238,35 @@
         let data = default_fungible_data();
         create_test_item(collection_id, &data.into());
 
-        assert_eq!(TemplateModule::fungible_item_id(collection_id, 1).owner, 1);
+        assert_eq!(TemplateModule::fungible_item_id(collection_id, 1).value, 5);
     });
 }
 
-#[test]
-fn create_multiple_fungible_items() {
-    new_test_ext().execute_with(|| {
-        default_limits();
+//#[test]
+// fn create_multiple_fungible_items() {
+//     new_test_ext().execute_with(|| {
+//         default_limits();
 
-        create_test_collection(&CollectionMode::Fungible(3), 1);
+//         create_test_collection(&CollectionMode::Fungible(3), 1);
 
-        let origin1 = Origin::signed(1);
+//         let origin1 = Origin::signed(1);
 
-        let items_data = vec![default_fungible_data(), default_fungible_data(), default_fungible_data()];
+//         let items_data = vec![default_fungible_data(), default_fungible_data(), default_fungible_data()];
 
-        assert_ok!(TemplateModule::create_multiple_items(
-            origin1.clone(),
-            1,
-            1,
-            items_data.clone().into_iter().map(|d| { d.into() }).collect()
-        ));
+//         assert_ok!(TemplateModule::create_multiple_items(
+//             origin1.clone(),
+//             1,
+//             1,
+//             items_data.clone().into_iter().map(|d| { d.into() }).collect()
+//         ));
         
-        for (index, _) in items_data.iter().enumerate() {
-            assert_eq!(TemplateModule::fungible_item_id(1, (index + 1) as TokenId).owner, 1);
-        }
-        assert_eq!(TemplateModule::balance_count(1, 1), 3000);
-        assert_eq!(TemplateModule::address_tokens(1, 1), [1, 2, 3]);
-    });
-}
+//         for (index, _) in items_data.iter().enumerate() {
+//             assert_eq!(TemplateModule::fungible_item_id(1, (index + 1) as TokenId).value, 5);
+//         }
+//         assert_eq!(TemplateModule::balance_count(1, 1), 3000);
+//         assert_eq!(TemplateModule::address_tokens(1, 1), [1, 2, 3]);
+//     });
+// }
 
 #[test]
 fn transfer_fungible_item() {
@@ -281,36 +281,26 @@
         let data = default_fungible_data();
         create_test_item(collection_id, &data.into());
 
-        assert_eq!(TemplateModule::fungible_item_id(1, 1).owner, 1);
-        assert_eq!(TemplateModule::balance_count(1, 1), 1000);
-        assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
+        assert_eq!(TemplateModule::fungible_item_id(1, 1).value, 5);
+        assert_eq!(TemplateModule::balance_count(1, 1), 5);
 
         // change owner scenario
-        assert_ok!(TemplateModule::transfer(origin1.clone(), 2, 1, 1, 1000));
-        assert_eq!(TemplateModule::fungible_item_id(1, 1).owner, 2);
-        assert_eq!(TemplateModule::fungible_item_id(1, 1).value, 1000);
+        assert_ok!(TemplateModule::transfer(origin1.clone(), 2, 1, 1, 5));
+        assert_eq!(TemplateModule::fungible_item_id(1, 1).value, 0);
         assert_eq!(TemplateModule::balance_count(1, 1), 0);
-        assert_eq!(TemplateModule::balance_count(1, 2), 1000);
-        // assert_eq!(TemplateModule::address_tokens(1, 1), []);
-        assert_eq!(TemplateModule::address_tokens(1, 2), [1]);
+        assert_eq!(TemplateModule::balance_count(1, 2), 5);
 
         // split item scenario
-        assert_ok!(TemplateModule::transfer(origin2.clone(), 3, 1, 1, 500));
-        assert_eq!(TemplateModule::fungible_item_id(1, 1).owner, 2);
-        assert_eq!(TemplateModule::fungible_item_id(1, 2).owner, 3);
-        assert_eq!(TemplateModule::balance_count(1, 2), 500);
-        assert_eq!(TemplateModule::balance_count(1, 3), 500);
-        assert_eq!(TemplateModule::address_tokens(1, 2), [1]);
-        assert_eq!(TemplateModule::address_tokens(1, 3), [2]);
+        assert_ok!(TemplateModule::transfer(origin2.clone(), 3, 1, 1, 3));
+        assert_eq!(TemplateModule::balance_count(1, 2), 2);
+        assert_eq!(TemplateModule::balance_count(1, 3), 3);
 
         // split item and new owner has account scenario
-        assert_ok!(TemplateModule::transfer(origin2.clone(), 3, 1, 1, 200));
-        assert_eq!(TemplateModule::fungible_item_id(1, 1).value, 300);
-        assert_eq!(TemplateModule::fungible_item_id(1, 2).value, 700);
-        assert_eq!(TemplateModule::balance_count(1, 2), 300);
-        assert_eq!(TemplateModule::balance_count(1, 3), 700);
-        assert_eq!(TemplateModule::address_tokens(1, 2), [1]);
-        assert_eq!(TemplateModule::address_tokens(1, 3), [2]);
+        assert_ok!(TemplateModule::transfer(origin2.clone(), 3, 1, 1, 1));
+        assert_eq!(TemplateModule::fungible_item_id(1, 2).value, 1);
+        assert_eq!(TemplateModule::fungible_item_id(1, 3).value, 4);
+        assert_eq!(TemplateModule::balance_count(1, 2), 1);
+        assert_eq!(TemplateModule::balance_count(1, 3), 4);
     });
 }
 
@@ -451,14 +441,11 @@
             1), Error::<Test>::NoPermission);
 
         // do approve
-        assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
-        assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
+        assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));
+        assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);
         assert_eq!(
-            TemplateModule::approved(1, (1, 1))[0],
-            ApprovePermissions {
-                approved: 2,
-                amount: 100000000
-            }
+            TemplateModule::approved(1, (1, 1, 2)),
+            5
         );
 
         assert_ok!(TemplateModule::transfer_from(
@@ -469,7 +456,7 @@
             1,
             1
         ));
-        assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 0);
+        assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 4);
     });
 }
 
@@ -505,17 +492,10 @@
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
 
         // do approve
-        assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
-        assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
-        assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1));
-        assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 2);
-        assert_eq!(
-            TemplateModule::approved(1, (1, 1))[0],
-            ApprovePermissions {
-                approved: 2,
-                amount: 100000000
-            }
-        );
+        assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));
+        assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);
+        assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1, 5));
+        assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 5);
 
         assert_ok!(TemplateModule::transfer_from(
             origin2.clone(),
@@ -525,7 +505,7 @@
             1,
             1
         ));
-        assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 0);
+        assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 4);
     });
 }
 
@@ -560,17 +540,10 @@
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
 
         // do approve
-        assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
-        assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
-        assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1));
-        assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 2);
-        assert_eq!(
-            TemplateModule::approved(1, (1, 1))[0],
-            ApprovePermissions {
-                approved: 2,
-                amount: 100000000
-            }
-        );
+        assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));
+        assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);
+        assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1, 1000));
+        assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 1000);
 
         assert_ok!(TemplateModule::transfer_from(
             origin2.clone(),
@@ -585,13 +558,9 @@
         assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
         assert_eq!(TemplateModule::address_tokens(1, 3), [1]);
 
-        assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
         assert_eq!(
-            TemplateModule::approved(1, (1, 1))[0],
-            ApprovePermissions {
-                approved: 3,
-                amount: 100000000
-            }
+            TemplateModule::approved(1, (1, 1, 3)),
+            900
         );
     });
 }
@@ -609,8 +578,7 @@
         let origin1 = Origin::signed(1);
         let origin2 = Origin::signed(2);
 
-        assert_eq!(TemplateModule::balance_count(1, 1), 1000);
-        assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
+        assert_eq!(TemplateModule::balance_count(1, 1), 5);
 
         assert_ok!(TemplateModule::set_mint_permission(
             origin1.clone(),
@@ -627,16 +595,13 @@
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
 
         // do approve
-        assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
-        assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
-        assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1));
-        assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 2);
+        assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));
+        assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);
+        assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1, 5));
+        assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 5);
         assert_eq!(
-            TemplateModule::approved(1, (1, 1))[0],
-            ApprovePermissions {
-                approved: 2,
-                amount: 100000000
-            }
+            TemplateModule::approved(1, (1, 1, 2)),
+            5
         );
 
         assert_ok!(TemplateModule::transfer_from(
@@ -645,37 +610,23 @@
             3,
             1,
             1,
-            100
+            4
         ));
-        assert_eq!(TemplateModule::balance_count(1, 1), 900);
-        assert_eq!(TemplateModule::balance_count(1, 3), 100);
-        assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
-        assert_eq!(TemplateModule::address_tokens(1, 3), [2]);
+        assert_eq!(TemplateModule::balance_count(1, 1), 1);
+        assert_eq!(TemplateModule::balance_count(1, 3), 4);
 
-        assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
-        assert_eq!(
-            TemplateModule::approved(1, (1, 1))[0],
-            ApprovePermissions {
-                approved: 3,
-                amount: 100000000
-            }
-        );
+        assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5);
+        assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 1);
 
-        assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
-        assert_ok!(TemplateModule::transfer_from(
+        assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5));
+        assert_noop!(TemplateModule::transfer_from(
             origin2.clone(),
             1,
             3,
             1,
             1,
-            900
-        ));
-        assert_eq!(TemplateModule::balance_count(1, 1), 0);
-        assert_eq!(TemplateModule::balance_count(1, 3), 1000);
-        // assert_eq!(TemplateModule::address_tokens(1, 1), []);
-        assert_eq!(TemplateModule::address_tokens(1, 3), [2]);
-
-        assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 0);
+            4
+        ), Error::<Test>::TokenValueNotEnough);
     });
 }
 
@@ -725,9 +676,9 @@
         assert_eq!(TemplateModule::balance_count(1, 1), 1);
 
         // burn item
-        assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1));
+        assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1, 5));
         assert_noop!(
-            TemplateModule::burn_item(origin1.clone(), 1, 1),
+            TemplateModule::burn_item(origin1.clone(), 1, 1, 5),
             Error::<Test>::TokenNotFound
         );
 
@@ -749,12 +700,12 @@
         create_test_item(collection_id, &data.into());
 
         // check balance (collection with id = 1, user id = 1)
-        assert_eq!(TemplateModule::balance_count(1, 1), 1000);
+        assert_eq!(TemplateModule::balance_count(1, 1), 5);
 
         // burn item
-        assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1));
+        assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1, 5));
         assert_noop!(
-            TemplateModule::burn_item(origin1.clone(), 1, 1),
+            TemplateModule::burn_item(origin1.clone(), 1, 1, 5),
             Error::<Test>::TokenNotFound
         );
 
@@ -791,9 +742,9 @@
         assert_eq!(TemplateModule::balance_count(1, 1), 1000);
 
         // burn item
-        assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1));
+        assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1, 1000));
         assert_noop!(
-            TemplateModule::burn_item(origin1.clone(), 1, 1),
+            TemplateModule::burn_item(origin1.clone(), 1, 1, 1000),
             Error::<Test>::TokenNotFound
         );
 
@@ -875,10 +826,10 @@
 
         // check balance (collection with id = 1, user id = 1)
         assert_eq!(TemplateModule::balance_count(nft_collection_id, 1), 1);
-        assert_eq!(TemplateModule::balance_count(fungible_collection_id, 1), 1000);
+        assert_eq!(TemplateModule::balance_count(fungible_collection_id, 1), 5);
         assert_eq!(TemplateModule::balance_count(re_fungible_collection_id, 1), 1000);
         assert_eq!(TemplateModule::nft_item_id(nft_collection_id, 1).owner, 1);
-        assert_eq!(TemplateModule::fungible_item_id(fungible_collection_id, 1).owner, 1);
+        assert_eq!(TemplateModule::fungible_item_id(fungible_collection_id, 1).value, 5);
         assert_eq!(TemplateModule::refungible_item_id(re_fungible_collection_id, 1).owner[0].owner, 1);
     });
 }
@@ -896,8 +847,8 @@
         let origin1 = Origin::signed(1);
         
         // approve
-        assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
-        assert_eq!(TemplateModule::approved(1, (1, 1))[0].approved, 2);
+        assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 1));
+        assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 1);
     });
 }
 
@@ -914,8 +865,8 @@
         create_test_item(collection_id, &data.into());
 
         // approve
-        assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
-        assert_eq!(TemplateModule::approved(1, (1, 1))[0].approved, 2);
+        assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 1));
+        assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 1);
 
         assert_ok!(TemplateModule::set_mint_permission(
             origin1.clone(),
@@ -1199,8 +1150,8 @@
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
 
         // do approve
-        assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1));
-        assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
+        assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1, 1));
+        assert_eq!(TemplateModule::approved(1, (1, 1, 1)), 1);
 
         assert_ok!(TemplateModule::remove_from_white_list(
             origin1.clone(),
@@ -1263,8 +1214,8 @@
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
 
         // do approve
-        assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1));
-        assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
+        assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1, 1));
+        assert_eq!(TemplateModule::approved(1, (1, 1, 1)), 1);
 
         assert_ok!(TemplateModule::remove_from_white_list(
             origin1.clone(),
@@ -1298,7 +1249,7 @@
             AccessMode::WhiteList
         ));
         assert_noop!(
-            TemplateModule::burn_item(origin1.clone(), 1, 1),
+            TemplateModule::burn_item(origin1.clone(), 1, 1, 5),
             Error::<Test>::AddresNotInWhiteList
         );
     });
@@ -1321,7 +1272,7 @@
 
         // do approve
         assert_noop!(
-            TemplateModule::approve(origin1.clone(), 1, 1, 1),
+            TemplateModule::approve(origin1.clone(), 1, 1, 1, 5),
             Error::<Test>::AddresNotInWhiteList
         );
     });
@@ -1374,8 +1325,8 @@
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
 
         // do approve
-        assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1));
-        assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
+        assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1, 5));
+        assert_eq!(TemplateModule::approved(1, (1, 1, 1)), 5);
 
         assert_ok!(TemplateModule::transfer_from(
             origin1.clone(),
modifiedtests/package.jsondiffbeforeafterboth
--- a/tests/package.json
+++ b/tests/package.json
@@ -20,9 +20,13 @@
     "load": "mocha --timeout 9999999 -r ts-node/register ./**/*.load.ts",
     "testSetSchemaVersion": "mocha --timeout 9999999 -r ts-node/register ./**/setSchemaVersion.test.ts",
     "testSetCollectionLimits": "mocha --timeout 9999999 -r ts-node/register ./**/setCollectionLimits.test.ts",
+    "testRemoveCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/removeCollectionAdmin.test.ts",
     "testConnection": "mocha --timeout 9999999 -r ts-node/register ./**/connection.test.ts",
     "testCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts",
-    "testCreateMultipleItems": "mocha --timeout 9999999 -r ts-node/register ./**/createMultipleItems.test.ts"
+    "testCreateMultipleItems": "mocha --timeout 9999999 -r ts-node/register ./**/createMultipleItems.test.ts",
+    "testApprove": "mocha --timeout 9999999 -r ts-node/register ./**/approve.test.ts",
+    "testTransferFrom": "mocha --timeout 9999999 -r ts-node/register ./**/transferFrom.test.ts",
+    "testCreateCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts"
   },
   "author": "",
   "license": "Apache 2.0",
addedtests/src/approve.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/approve.test.ts
@@ -0,0 +1,140 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+import { ApiPromise } from '@polkadot/api';
+import BN from 'bn.js';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import privateKey from './substrate/privateKey';
+import { default as usingApi } from './substrate/substrate-api';
+import {
+  approveExpectFail,
+  approveExpectSuccess,
+  createCollectionExpectSuccess,
+  createItemExpectSuccess,
+  destroyCollectionExpectSuccess,
+} from './util/helpers';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+describe('Integration Test approve(spender, collection_id, item_id, amount):', () => {
+  it('Execute the extrinsic and check approvedList', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      const nftCollectionId = await createCollectionExpectSuccess();
+      // nft
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob);
+      // fungible
+      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+      const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob);
+      // reFungible
+      const reFungibleCollectionId =
+        await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+      await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob);
+    });
+  });
+
+  it('Remove approval by using 0 amount', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      const nftCollectionId = await createCollectionExpectSuccess();
+      // nft
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob, 1);
+      await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob, 0);
+      // fungible
+      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+      const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1);
+      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 0);
+      // reFungible
+      const reFungibleCollectionId =
+        await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+      await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob, 1);
+      await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob, 0);
+    });
+  });
+});
+
+describe('Negative Integration Test approve(spender, collection_id, item_id, amount):', () => {
+  it('Approve for a collection that does not exist', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      // nft
+      const nftCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+      await approveExpectFail(nftCollectionCount + 1, 1, Alice, Bob);
+      // fungible
+      const fungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+      await approveExpectFail(fungibleCollectionCount + 1, 1, Alice, Bob);
+      // reFungible
+      const reFungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+      await approveExpectFail(reFungibleCollectionCount + 1, 1, Alice, Bob);
+    });
+  });
+
+  it('Approve for a collection that was destroyed', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      await destroyCollectionExpectSuccess(nftCollectionId);
+      await approveExpectFail(nftCollectionId, 1, Alice, Bob);
+      // fungible
+      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+      await destroyCollectionExpectSuccess(fungibleCollectionId);
+      await approveExpectFail(fungibleCollectionId, 1, Alice, Bob);
+      // reFungible
+      const reFungibleCollectionId =
+        await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      await destroyCollectionExpectSuccess(reFungibleCollectionId);
+      await approveExpectFail(reFungibleCollectionId, 1, Alice, Bob);
+    });
+  });
+
+  it('Approve transfer of a token that does not exist', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      await approveExpectFail(nftCollectionId, 2, Alice, Bob);
+      // fungible
+      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+      await approveExpectFail(fungibleCollectionId, 2, Alice, Bob);
+      // reFungible
+      const reFungibleCollectionId =
+        await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      await approveExpectFail(reFungibleCollectionId, 2, Alice, Bob);
+    });
+  });
+
+  it('Approve using the address that does not own the approved token', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      const nftCollectionId = await createCollectionExpectSuccess();
+      // nft
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      await approveExpectFail(nftCollectionId, newNftTokenId, Bob, Alice);
+      // fungible
+      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+      const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+      await approveExpectFail(fungibleCollectionId, newFungibleTokenId, Bob, Alice);
+      // reFungible
+      const reFungibleCollectionId =
+        await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+      await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, Bob, Alice);
+    });
+  });
+});
addedtests/src/burnItem.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/burnItem.test.ts
@@ -0,0 +1,237 @@
+import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from './substrate/substrate-api';
+import { Keyring } from "@polkadot/api";
+import { IKeyringPair } from "@polkadot/types/types";
+import { 
+  createCollectionExpectSuccess, 
+  createItemExpectSuccess,
+  getGenericResult,
+  destroyCollectionExpectSuccess
+} from './util/helpers';
+import { nullPublicKey } from "./accounts";
+
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+let alice: IKeyringPair;
+let bob: IKeyringPair;
+
+describe('integration test: ext. burnItem():', () => {
+  before(async () => {
+    await usingApi(async (api) => {
+      const keyring = new Keyring({ type: 'sr25519' });
+      alice = keyring.addFromUri(`//Alice`);
+      bob = keyring.addFromUri(`//Bob`);
+    });
+  });
+
+  it('Burn item in NFT collection', async () => {
+    const createMode = 'NFT';
+    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode}});
+    const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
+
+    await usingApi(async (api) => {
+      const tx = api.tx.nft.burnItem(collectionId, tokenId, 0);
+      const events = await submitTransactionAsync(alice, tx);
+      const result = getGenericResult(events);
+  
+      // Get the item 
+      const item: any = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON();
+ 
+      // What to expect
+      expect(result.success).to.be.true;
+      expect(item).to.be.not.null;
+      expect(item.Owner).to.be.equal(nullPublicKey);
+    });
+  
+  });
+  it('Burn item in Fungible collection', async () => {
+    const createMode = 'Fungible';
+    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0 }});
+    await createItemExpectSuccess(alice, collectionId, createMode); // Helper creates 10 fungible tokens
+    const tokenId = 0; // ignored
+
+    await usingApi(async (api) => {
+      // Destroy 1 of 10
+      const tx = api.tx.nft.burnItem(collectionId, tokenId, 1);
+      const events = await submitTransactionAsync(alice, tx);
+      const result = getGenericResult(events);
+  
+      // Get alice balance 
+      const balance: any = (await api.query.nft.fungibleItemList(collectionId, alice.address)).toJSON();
+ 
+      // What to expect
+      expect(result.success).to.be.true;
+      expect(balance).to.be.not.null;
+      expect(balance.Value).to.be.equal(9);
+    });
+
+  });
+  it('Burn item in ReFungible collection', async () => {
+    const createMode = 'ReFungible';
+    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 2 }});
+    const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
+
+    await usingApi(async (api) => {
+      const tx = api.tx.nft.burnItem(collectionId, tokenId, 1);
+      const events = await submitTransactionAsync(alice, tx);
+      const result = getGenericResult(events);
+  
+      // Get alice balance 
+      const balance: any = (await api.query.nft.reFungibleItemList(collectionId, tokenId)).toJSON();
+      
+      // What to expect
+      expect(result.success).to.be.true;
+      expect(balance).to.be.not.null;
+      expect(balance.Owner.length).to.be.equal(0);
+    });
+
+  });
+
+  it('Burn owned portion of item in ReFungible collection', async () => {
+    const createMode = 'ReFungible';
+    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 2 }});
+    const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
+
+    await usingApi(async (api) => {
+      // Transfer 1/100 of the token to Bob
+      const transfertx = api.tx.nft.transfer(bob.address, collectionId, tokenId, 1);
+      const events1 = await submitTransactionAsync(alice, transfertx);
+      const result1 = getGenericResult(events1);
+
+      // Get balances
+      const balanceBefore: any = (await api.query.nft.reFungibleItemList(collectionId, tokenId)).toJSON();
+
+      // Bob burns his portion
+      const tx = api.tx.nft.burnItem(collectionId, tokenId, 0);
+      const events2 = await submitTransactionAsync(bob, tx);
+      const result2 = getGenericResult(events2);
+  
+      // Get balances 
+      const balance: any = (await api.query.nft.reFungibleItemList(collectionId, tokenId)).toJSON();
+      // console.log(balance);
+      
+      // What to expect before burning
+      expect(result1.success).to.be.true;
+      expect(balanceBefore).to.be.not.null;
+      expect(balanceBefore.Owner.length).to.be.equal(2);
+      expect(balanceBefore.Owner[0].Owner).to.be.equal(alice.address);
+      expect(balanceBefore.Owner[0].Fraction).to.be.equal(99);
+      expect(balanceBefore.Owner[1].Owner).to.be.equal(bob.address);
+      expect(balanceBefore.Owner[1].Fraction).to.be.equal(1);
+
+      // What to expect after burning
+      expect(result2.success).to.be.true;
+      expect(balance).to.be.not.null;
+      expect(balance.Owner.length).to.be.equal(1);
+      expect(balance.Owner[0].Fraction).to.be.equal(99);
+      expect(balance.Owner[0].Owner).to.be.equal(alice.address);
+    });
+
+  });
+
+});
+
+describe('Negative integration test: ext. burnItem():', () => {
+  before(async () => {
+    await usingApi(async (api) => {
+      const keyring = new Keyring({ type: 'sr25519' });
+      alice = keyring.addFromUri(`//Alice`);
+      bob = keyring.addFromUri(`//Bob`);
+    });
+  });
+
+  it('Burn a token in a destroyed collection', async () => {
+    const createMode = 'NFT';
+    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode }});
+    const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
+    await destroyCollectionExpectSuccess(collectionId);
+
+    await usingApi(async (api) => {
+      const tx = api.tx.nft.burnItem(collectionId, tokenId, 0);
+      const badTransaction = async function () { 
+        await submitTransactionExpectFailAsync(alice, tx);
+      };
+      await expect(badTransaction()).to.be.rejected;
+    });
+
+  });
+
+  it('Burn a token that was never created', async () => {
+    const createMode = 'NFT';
+    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode }});
+    const tokenId = 10;
+
+    await usingApi(async (api) => {
+      const tx = api.tx.nft.burnItem(collectionId, tokenId, 0);
+      const badTransaction = async function () { 
+        await submitTransactionExpectFailAsync(alice, tx);
+      };
+      await expect(badTransaction()).to.be.rejected;
+    });
+
+  });
+
+  it('Burn a token using the address that does not own it', async () => {
+    const createMode = 'NFT';
+    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode }});
+    const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
+
+    await usingApi(async (api) => {
+      const tx = api.tx.nft.burnItem(collectionId, tokenId, 0);
+      const badTransaction = async function () { 
+        await submitTransactionExpectFailAsync(bob, tx);
+      };
+      await expect(badTransaction()).to.be.rejected;
+    });
+
+  });
+
+  it('Transfer a burned a token', async () => {
+    const createMode = 'NFT';
+    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode }});
+    const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
+
+    await usingApi(async (api) => {
+
+      const burntx = api.tx.nft.burnItem(collectionId, tokenId, 0);
+      const events1 = await submitTransactionAsync(alice, burntx);
+      const result1 = getGenericResult(events1);
+      expect(result1.success).to.be.true;
+  
+      const tx = api.tx.nft.transfer(bob.address, collectionId, tokenId, 0);
+      const badTransaction = async function () { 
+        await submitTransactionExpectFailAsync(alice, tx);
+      };
+      await expect(badTransaction()).to.be.rejected;
+    });
+
+  });
+
+  it('Burn more than owned in Fungible collection', async () => {
+    const createMode = 'Fungible';
+    const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0 }});
+    // Helper creates 10 fungible tokens
+    await createItemExpectSuccess(alice, collectionId, createMode);
+    const tokenId = 0; // ignored
+
+    await usingApi(async (api) => {
+      // Destroy 11 of 10
+      const tx = api.tx.nft.burnItem(collectionId, tokenId, 11);
+      const badTransaction = async function () { 
+        await submitTransactionExpectFailAsync(alice, tx);
+      };
+      await expect(badTransaction()).to.be.rejected;
+      
+      // Get alice balance 
+      const balance: any = (await api.query.nft.fungibleItemList(collectionId, alice.address)).toJSON();
+ 
+      // What to expect
+      expect(balance).to.be.not.null;
+      expect(balance.Value).to.be.equal(10);
+    });
+
+  });
+
+});
modifiedtests/src/confirmSponsorship.test.tsdiffbeforeafterboth
--- a/tests/src/confirmSponsorship.test.ts
+++ b/tests/src/confirmSponsorship.test.ts
@@ -89,7 +89,7 @@
   });
 
   it('Fungible: Transfer fees are paid by the sponsor after confirmation', async () => {
-    const collectionId = await createCollectionExpectSuccess({mode: 'Fungible'});
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0 }});
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
     await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
 
@@ -115,7 +115,7 @@
   });
 
   it('ReFungible: Transfer fees are paid by the sponsor after confirmation', async () => {
-    const collectionId = await createCollectionExpectSuccess({mode: 'ReFungible'});
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0 }});
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
     await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
 
@@ -210,7 +210,7 @@
   });
 
   it('Fungible: Sponsoring is rate limited', async () => {
-    const collectionId = await createCollectionExpectSuccess({mode: 'Fungible'});
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0 }});
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
     await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
 
@@ -247,7 +247,7 @@
   });
 
   it('ReFungible: Sponsoring is rate limited', async () => {
-    const collectionId = await createCollectionExpectSuccess({mode: 'ReFungible'});
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0 }});
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
     await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
 
modifiedtests/src/createCollection.test.tsdiffbeforeafterboth
--- a/tests/src/createCollection.test.ts
+++ b/tests/src/createCollection.test.ts
@@ -5,15 +5,15 @@
 
 import chai from 'chai';
 import chaiAsPromised from 'chai-as-promised';
-import { default as usingApi } from "./substrate/substrate-api";
-import { createCollectionExpectSuccess, createCollectionExpectFailure, CollectionMode } from "./util/helpers";
+import { default as usingApi } from './substrate/substrate-api';
+import { createCollectionExpectFailure, createCollectionExpectSuccess } from './util/helpers';
 
 chai.use(chaiAsPromised);
 const expect = chai.expect;
 
 describe('integration test: ext. createCollection():', () => {
   it('Create new NFT collection', async () => {
-    await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: 'NFT'});
+    await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}});
   });
   it('Create new NFT collection whith collection_name of maximum length (64 bytes)', async () => {
     await createCollectionExpectSuccess({name: 'A'.repeat(64)});
@@ -25,34 +25,35 @@
     await createCollectionExpectSuccess({tokenPrefix: 'A'.repeat(16)});
   });
   it('Create new Fungible collection', async () => {
-    await createCollectionExpectSuccess({mode: 'Fungible'});
+    await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
   });
   it('Create new ReFungible collection', async () => {
-    await createCollectionExpectSuccess({mode: 'ReFungible'});
+    await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
   });
 });
 
 describe('(!negative test!) integration test: ext. createCollection():', () => {
   it('(!negative test!) create new NFT collection whith incorrect data (mode)', async () => {
     await usingApi(async (api) => {
-      const AcollectionCount = parseInt((await api.query.nft.collectionCount()).toString());
+      const AcollectionCount = parseInt((await api.query.nft.collectionCount()).toString(), 10);
 
-      const badTransaction = async function () { 
-        await createCollectionExpectSuccess({mode: 'BadMode' as CollectionMode});
+      const badTransaction = async () => {
+        await createCollectionExpectSuccess({mode: {type: 'Invalid'}});
       };
+      // tslint:disable-next-line:no-unused-expression
       expect(badTransaction()).to.be.rejected;
 
-      const BcollectionCount = parseInt((await api.query.nft.collectionCount()).toString());
+      const BcollectionCount = parseInt((await api.query.nft.collectionCount()).toString(), 10);
       expect(BcollectionCount).to.be.equal(AcollectionCount, 'Error: Incorrect collection created.');
     });
   });
   it('(!negative test!) create new NFT collection whith incorrect data (collection_name)', async () => {
-    await createCollectionExpectFailure({name: 'A'.repeat(65)});
+    await createCollectionExpectFailure({ name: 'A'.repeat(65), mode: {type: 'NFT'}});
   });
   it('(!negative test!) create new NFT collection whith incorrect data (collection_description)', async () => {
-    await createCollectionExpectFailure({description: 'A'.repeat(257)});
+    await createCollectionExpectFailure({ description: 'A'.repeat(257), mode: { type: 'NFT' }});
   });
   it('(!negative test!) create new NFT collection whith incorrect data (token_prefix)', async () => {
-    await createCollectionExpectFailure({tokenPrefix: 'A'.repeat(17)});
+    await createCollectionExpectFailure({tokenPrefix: 'A'.repeat(17), mode: {type: 'NFT'}});
   });
 });
modifiedtests/src/createItem.test.tsdiffbeforeafterboth
--- a/tests/src/createItem.test.ts
+++ b/tests/src/createItem.test.ts
@@ -18,17 +18,17 @@
 
   it('Create new item in NFT collection', async () => {
     const createMode = 'NFT';
-    const newCollectionID = await createCollectionExpectSuccess({mode: createMode});
+    const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode}});
     await createItemExpectSuccess(alice, newCollectionID, createMode);
   });
   it('Create new item in Fungible collection', async () => {
     const createMode = 'Fungible';
-    const newCollectionID = await createCollectionExpectSuccess({mode: createMode});
+    const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0}});
     await createItemExpectSuccess(alice, newCollectionID, createMode);
   });
   it('Create new item in ReFungible collection', async () => {
     const createMode = 'ReFungible';
-    const newCollectionID = await createCollectionExpectSuccess({mode: createMode});
+    const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0}});
     await createItemExpectSuccess(alice, newCollectionID, createMode);
   });
 });
modifiedtests/src/destroyCollection.test.tsdiffbeforeafterboth
--- a/tests/src/destroyCollection.test.ts
+++ b/tests/src/destroyCollection.test.ts
@@ -14,11 +14,11 @@
     await destroyCollectionExpectSuccess(collectionId);
   });
   it('Fungible collection can be destroyed', async () => {
-    const collectionId = await createCollectionExpectSuccess({ mode: 'Fungible' });
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
     await destroyCollectionExpectSuccess(collectionId);
   });
   it('ReFungible collection can be destroyed', async () => {
-    const collectionId = await createCollectionExpectSuccess({ mode: 'ReFungible' });
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
     await destroyCollectionExpectSuccess(collectionId);
   });
 });
addedtests/src/removeCollectionAdmin.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/removeCollectionAdmin.test.ts
@@ -0,0 +1,82 @@
+import { ApiPromise } from '@polkadot/api';
+import BN from 'bn.js';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import privateKey from './substrate/privateKey';
+import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from './substrate/substrate-api';
+import {createCollectionExpectSuccess, destroyCollectionExpectSuccess} from './util/helpers';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+describe('Integration Test removeCollectionAdmin(collection_id, account_id):', () => {
+  it('Remove collection admin.', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const collectionId = await createCollectionExpectSuccess();
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      const collection: any = (await api.query.nft.collection(collectionId));
+      expect(collection.Owner.toString()).to.be.eq(Alice.address);
+      // first - add collection admin Bob
+      const addAdminTx = api.tx.nft.addCollectionAdmin(collectionId, Bob.address);
+      await submitTransactionAsync(Alice, addAdminTx);
+
+      const adminListAfterAddAdmin: any = (await api.query.nft.adminList(collectionId));
+      expect(adminListAfterAddAdmin).to.be.contains(Bob.address);
+
+      // then remove bob from admins of collection
+      const removeAdminTx = api.tx.nft.removeCollectionAdmin(collectionId, Bob.address);
+      await submitTransactionAsync(Alice, removeAdminTx);
+
+      const adminListAfterRemoveAdmin: any = (await api.query.nft.adminList(collectionId));
+      expect(adminListAfterRemoveAdmin).not.to.be.contains(Bob.address);
+    });
+  });
+});
+
+describe('Negative Integration Test removeCollectionAdmin(collection_id, account_id):', () => {
+  it('Can\'t remove collection admin from not existing collection', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      // tslint:disable-next-line: no-bitwise
+      const collectionId = (1 << 32) - 1;
+      const alice = privateKey('//Alice');
+      const bob = privateKey('//Bob');
+
+      const changeOwnerTx = api.tx.nft.removeCollectionAdmin(collectionId, bob.address);
+      await expect(submitTransactionExpectFailAsync(alice, changeOwnerTx)).to.be.rejected;
+
+      // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
+      await createCollectionExpectSuccess();
+    });
+  });
+
+  it('Can\'t remove collection admin from deleted collection', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      // tslint:disable-next-line: no-bitwise
+      const collectionId = await createCollectionExpectSuccess();
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+
+      await destroyCollectionExpectSuccess(collectionId);
+
+      const changeOwnerTx = api.tx.nft.removeCollectionAdmin(collectionId, Bob.address);
+      await expect(submitTransactionExpectFailAsync(Alice, changeOwnerTx)).to.be.rejected;
+
+      // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
+      await createCollectionExpectSuccess();
+    });
+  });
+
+  it('Remove admin from collection that has no admins', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const collectionId = await createCollectionExpectSuccess();
+
+      const adminListBeforeAddAdmin: any = (await api.query.nft.adminList(collectionId));
+      expect(adminListBeforeAddAdmin).to.have.lengthOf(0);
+
+      const tx = api.tx.nft.removeCollectionAdmin(collectionId, Alice.address);
+      await expect(submitTransactionExpectFailAsync(Alice, tx)).to.be.rejected;
+    });
+  });
+});
modifiedtests/src/setCollectionLimits.test.tsdiffbeforeafterboth
--- a/tests/src/setCollectionLimits.test.ts
+++ b/tests/src/setCollectionLimits.test.ts
@@ -32,7 +32,7 @@
   });
   it('choose or create collection for testing', async () => {
     await usingApi(async () => {
-      collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: 'NFT'});
+      collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}});
     });
   });
 });
modifiedtests/src/setCollectionSponsor.test.tsdiffbeforeafterboth
--- a/tests/src/setCollectionSponsor.test.ts
+++ b/tests/src/setCollectionSponsor.test.ts
@@ -30,11 +30,11 @@
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
   });
   it('Set Fungible collection sponsor', async () => {
-    const collectionId = await createCollectionExpectSuccess({ mode: 'Fungible' });
+    const collectionId = await createCollectionExpectSuccess({ mode: {type: 'Fungible', decimalPoints: 0} });
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
   });
   it('Set ReFungible collection sponsor', async () => {
-    const collectionId = await createCollectionExpectSuccess({ mode: 'ReFungible' });
+    const collectionId = await createCollectionExpectSuccess({ mode: {type: 'ReFungible', decimalPoints: 0} });
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
   });
 
modifiedtests/src/setSchemaVersion.test.tsdiffbeforeafterboth
--- a/tests/src/setSchemaVersion.test.ts
+++ b/tests/src/setSchemaVersion.test.ts
@@ -35,7 +35,7 @@
   });
   it('choose or create collection for testing', async () => {
     await usingApi(async () => {
-      collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: 'NFT'});
+      collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}});
     });
   });
 });
@@ -93,12 +93,6 @@
       const nonExistedCollectionId = collectionCount + 1;
       tx = api.tx.nft.setSchemaVersion(nonExistedCollectionId, 'ImageURL');
       await expect(submitTransactionExpectFailAsync(alice, tx)).to.be.rejected;
-      /*try {
-        await submitTransactionAsync(alice, tx);
-      } catch (e) {
-        // tslint:disable-next-line:no-unused-expression
-        expect(e).to.be.exist;
-      }*/
     });
   });
 
@@ -119,13 +113,6 @@
       await destroyCollectionExpectSuccess(collectionIdForTesting);
       tx = api.tx.nft.setSchemaVersion(collectionIdForTesting, 'ImageURL');
       await expect(submitTransactionExpectFailAsync(alice, tx)).to.be.rejected;
-      /*try {
-        tx = api.tx.nft.setSchemaVersion(collectionIdForTesting, 'ImageURL');
-        await submitTransactionAsync(alice, tx);
-      } catch (e) {
-        // tslint:disable-next-line:no-unused-expression
-        expect(e).to.be.exist;
-      }*/
     });
   });
 });
modifiedtests/src/substrate/substrate-api.tsdiffbeforeafterboth
--- a/tests/src/substrate/substrate-api.ts
+++ b/tests/src/substrate/substrate-api.ts
@@ -90,7 +90,6 @@
         res(rec);
         console.error = consoleError;
         console.log = consoleLog;
-        
       });
     };
     const reject = (errror: any) => {
@@ -104,6 +103,8 @@
       await transaction.signAndSend(sender, ({ events = [], status }) => {
         const transactionStatus = getTransactionStatus(events, status);
 
+        console.log('transactionStatus', transactionStatus, 'events', events);
+
         if (transactionStatus == TransactionStatus.Success) {
           resolve(events);
         } else if (transactionStatus == TransactionStatus.Fail) {
@@ -114,4 +115,4 @@
       reject(e);
     }
   });
-}
\ No newline at end of file
+}
addedtests/src/transferFrom.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/transferFrom.test.ts
@@ -0,0 +1,188 @@
+//
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE', which is part of this source code package.
+//
+import { ApiPromise } from '@polkadot/api';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import privateKey from './substrate/privateKey';
+import { default as usingApi } from './substrate/substrate-api';
+import {
+  approveExpectFail,
+  approveExpectSuccess,
+  createCollectionExpectSuccess,
+  createItemExpectSuccess,
+  destroyCollectionExpectSuccess,
+  transferFromExpectFail,
+  transferFromExpectSuccess,
+} from './util/helpers';
+
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+describe('Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => {
+  it('Execute the extrinsic and check nftItemList - owner of token', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      const Charlie = privateKey('//CHARLIE');
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob);
+
+      await transferFromExpectSuccess(nftCollectionId, newNftTokenId, Bob, Alice, Charlie, 1, 'NFT');
+
+      // fungible
+      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+      const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob);
+      await transferFromExpectSuccess(fungibleCollectionId, newFungibleTokenId, Bob, Alice, Charlie, 1, 'Fungible');
+      // reFungible
+      const reFungibleCollectionId = await
+        createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+      await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob);
+      await transferFromExpectSuccess(reFungibleCollectionId,
+        newReFungibleTokenId, Bob, Alice, Charlie, 1, 'ReFungible');
+    });
+  });
+});
+
+describe('Negative Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => {
+  it('transferFrom for a collection that does not exist', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      const Charlie = privateKey('//CHARLIE');
+      // nft
+      const nftCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+      await approveExpectFail(nftCollectionCount + 1, 1, Alice, Bob);
+
+      await transferFromExpectFail(nftCollectionCount + 1, 1, Bob, Alice, Charlie, 1);
+
+      // fungible
+      const fungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+      await approveExpectFail(fungibleCollectionCount + 1, 1, Alice, Bob);
+
+      await transferFromExpectFail(fungibleCollectionCount + 1, 1, Bob, Alice, Charlie, 1);
+      // reFungible
+      const reFungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
+      await approveExpectFail(reFungibleCollectionCount + 1, 1, Alice, Bob);
+
+      await transferFromExpectFail(reFungibleCollectionCount + 1, 1, Bob, Alice, Charlie, 1);
+    });
+  });
+
+  /* it('transferFrom for a collection that was destroyed', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      this test copies approve negative test
+    });
+  }); */
+
+  /* it('transferFrom a token that does not exist', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      this test copies approve negative test
+    });
+  }); */
+
+  /* it('transferFrom a token that was deleted', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      this test copies approve negative test
+    });
+  }); */
+
+  it('transferFrom for not approved address', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      const Charlie = privateKey('//CHARLIE');
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+
+      await transferFromExpectFail(nftCollectionId, newNftTokenId, Bob, Alice, Charlie, 1);
+
+      // fungible
+      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+      const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+      await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, Bob, Alice, Charlie, 1);
+      // reFungible
+      const reFungibleCollectionId = await
+        createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+      await transferFromExpectFail(reFungibleCollectionId,
+        newReFungibleTokenId, Bob, Alice, Charlie, 1);
+    });
+  });
+
+  it('transferFrom incorrect token count', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      const Charlie = privateKey('//CHARLIE');
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob);
+
+      await transferFromExpectFail(nftCollectionId, newNftTokenId, Bob, Alice, Charlie, 2);
+
+      // fungible
+      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+      const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob);
+      await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, Bob, Alice, Charlie, 2);
+      // reFungible
+      const reFungibleCollectionId = await
+        createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+      await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob);
+      await transferFromExpectFail(reFungibleCollectionId,
+        newReFungibleTokenId, Bob, Alice, Charlie, 2);
+    });
+  });
+
+  it('execute transferFrom from account that is not owner of collection', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const Alice = privateKey('//Alice');
+      const Bob = privateKey('//Bob');
+      const Charlie = privateKey('//CHARLIE');
+      const Dave = privateKey('//DAVE');
+      // nft
+      const nftCollectionId = await createCollectionExpectSuccess();
+      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+      try {
+        await approveExpectFail(nftCollectionId, newNftTokenId, Dave, Bob);
+        await transferFromExpectFail(nftCollectionId, newNftTokenId, Dave, Alice, Charlie, 1);
+      } catch (e) {
+        // tslint:disable-next-line:no-unused-expression
+        expect(e).to.be.exist;
+      }
+
+      // await transferFromExpectFail(nftCollectionId, newNftTokenId, Dave, Alice, Charlie, 1);
+
+      // fungible
+      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+      const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+      try {
+        await approveExpectFail(fungibleCollectionId, newFungibleTokenId, Dave, Bob);
+        await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, Dave, Alice, Charlie, 1);
+      } catch (e) {
+        // tslint:disable-next-line:no-unused-expression
+        expect(e).to.be.exist;
+      }
+      // reFungible
+      const reFungibleCollectionId = await
+        createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+      try {
+        await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, Dave, Bob);
+        await transferFromExpectFail(reFungibleCollectionId, newReFungibleTokenId, Dave, Alice, Charlie, 1);
+      } catch (e) {
+        // tslint:disable-next-line:no-unused-expression
+        expect(e).to.be.exist;
+      }
+    });
+  });
+});
modifiedtests/src/util/helpers.tsdiffbeforeafterboth
--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -3,20 +3,20 @@
 // file 'LICENSE', which is part of this source code package.
 //
 
-import chai from 'chai';
-import chaiAsPromised from 'chai-as-promised';
+import { ApiPromise, Keyring } from '@polkadot/api';
+import { Enum, Struct } from '@polkadot/types/codec';
 import type { AccountId, EventRecord } from '@polkadot/types/interfaces';
-import { ApiPromise, Keyring } from "@polkadot/api";
-import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from "../substrate/substrate-api";
-import privateKey from '../substrate/privateKey';
-import { alicesPublicKey, nullPublicKey } from "../accounts";
-import { strToUTF16, utf16ToStr, hexToStr } from './util';
+import { u128 } from '@polkadot/types/primitive';
 import { IKeyringPair } from '@polkadot/types/types';
 import { BigNumber } from 'bignumber.js';
-import { Struct, Enum } from '@polkadot/types/codec';
-import { u128 } from '@polkadot/types/primitive';
+import BN from 'bn.js';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import { alicesPublicKey, nullPublicKey } from '../accounts';
+import privateKey from '../substrate/privateKey';
+import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from '../substrate/substrate-api';
 import { ICollectionInterface } from '../types';
-import BN from "bn.js";
+import { hexToStr, strToUTF16, utf16ToStr } from './util';
 
 chai.use(chaiAsPromised);
 const expect = chai.expect;
@@ -25,24 +25,45 @@
   success: boolean,
 };
 
-type CreateCollectionResult = {
-  success: boolean,
-  collectionId: number
-};
+interface CreateCollectionResult {
+  success: boolean;
+  collectionId: number;
+}
+
+interface CreateItemResult {
+  success: boolean;
+  collectionId: number;
+  itemId: number;
+}
+
+interface IReFungibleOwner {
+  Fraction: BN;
+  Owner: number[];
+}
+
+interface ITokenDataType {
+  Owner: number[];
+  ConstData: number[];
+  VariableData: number[];
+}
+
+interface IFungibleTokenDataType {
+  Value: BN;
+}
 
-type CreateItemResult = {
-  success: boolean,
-  collectionId: number,
-  itemId: number
-};
+interface IReFungibleTokenDataType {
+  Owner: IReFungibleOwner[];
+  ConstData: number[];
+  VariableData: number[];
+}
 
 export function getGenericResult(events: EventRecord[]): GenericResult {
-  let result: GenericResult = {
-    success: false
-  }
+  const result: GenericResult = {
+    success: false,
+  };
   events.forEach(({ phase, event: { data, method, section } }) => {
     // console.log(`    ${phase}: ${section}.${method}:: ${data}`);
-    if (method == 'ExtrinsicSuccess') {
+    if (method === 'ExtrinsicSuccess') {
       result.success = true;
     }
   });
@@ -60,10 +81,10 @@
       collectionId = parseInt(data[0].toString());
     }
   });
-  let result: CreateCollectionResult = {
+  const result: CreateCollectionResult = {
     success,
-    collectionId
-  }
+    collectionId,
+  };
   return result;
 }
 
@@ -80,27 +101,60 @@
       itemId = parseInt(data[1].toString());
     }
   });
-  let result: CreateItemResult = {
+  const result: CreateItemResult = {
     success,
     collectionId,
-    itemId
-  }
+    itemId,
+  };
   return result;
 }
 
-export type CollectionMode = 'NFT' | 'Fungible' | 'ReFungible';
+interface Invalid {
+  type: 'Invalid';
+}
+
+interface Nft {
+  type: 'NFT';
+}
+
+interface Fungible {
+  type: 'Fungible';
+  decimalPoints: number;
+}
+
+interface ReFungible {
+  type: 'ReFungible';
+  decimalPoints: number;
+}
+
+interface Nft {
+  type: 'NFT'
+}
+
+interface Fungible {
+  type: 'Fungible',
+  decimalPoints: number
+}
+
+interface ReFungible {
+  type: 'ReFungible',
+  decimalPoints: number
+}
+
+type CollectionMode = Nft | Fungible | ReFungible | Invalid;
+
 export type CreateCollectionParams = {
   mode: CollectionMode,
   name: string,
   description: string,
-  tokenPrefix: string
+  tokenPrefix: string,
 };
 
 const defaultCreateCollectionParams: CreateCollectionParams = {
+  description: 'description',
+  mode: { type: 'NFT' },
   name: 'name',
-  description: 'description',
-  mode: 'NFT',
-  tokenPrefix: 'prefix'
+  tokenPrefix: 'prefix',
 }
 
 export async function createCollectionExpectSuccess(params: Partial<CreateCollectionParams> = {}): Promise<number> {
@@ -109,25 +163,39 @@
   let collectionId: number = 0;
   await usingApi(async (api) => {
     // Get number of collections before the transaction
-    const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());
+    const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString(), 10);
 
     // Run the CreateCollection transaction
     const alicePrivateKey = privateKey('//Alice');
-    const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), mode);
+
+    let modeprm = {};
+    if (mode.type === 'NFT') {
+      modeprm = {nft: null};
+    } else if (mode.type === 'Fungible') {
+      modeprm = {fungible: mode.decimalPoints};
+    } else if (mode.type === 'ReFungible') {
+      modeprm = {refungible: mode.decimalPoints};
+    } else if (mode.type === 'Invalid') {
+      modeprm = {invalid: null};
+    }
+
+    const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), modeprm);
     const events = await submitTransactionAsync(alicePrivateKey, tx);
     const result = getCreateCollectionResult(events);
 
     // Get number of collections after the transaction
-    const BcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());
+    const BcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString(), 10);
 
-    // Get the collection 
+    // Get the collection
     const collection: any = (await api.query.nft.collection(result.collectionId)).toJSON();
 
     // What to expect
+    // tslint:disable-next-line:no-unused-expression
     expect(result.success).to.be.true;
     expect(result.collectionId).to.be.equal(BcollectionCount);
+    // tslint:disable-next-line:no-unused-expression
     expect(collection).to.be.not.null;
-    expect(BcollectionCount).to.be.equal(AcollectionCount+1, 'Error: NFT collection NOT created.');
+    expect(BcollectionCount).to.be.equal(AcollectionCount + 1, 'Error: NFT collection NOT created.');
     expect(collection.Owner).to.be.equal(alicesPublicKey);
     expect(utf16ToStr(collection.Name)).to.be.equal(name);
     expect(utf16ToStr(collection.Description)).to.be.equal(description);
@@ -138,17 +206,28 @@
 
   return collectionId;
 }
-  
+
 export async function createCollectionExpectFailure(params: Partial<CreateCollectionParams> = {}) {
   const {name, description, mode, tokenPrefix } = {...defaultCreateCollectionParams, ...params};
 
+  let modeprm = {};
+  if (mode.type === 'NFT') {
+    modeprm = {nft: null};
+  } else if (mode.type === 'Fungible') {
+    modeprm = {fungible: mode.decimalPoints};
+  } else if (mode.type === 'ReFungible') {
+    modeprm = {refungible: mode.decimalPoints};
+  } else if (mode.type === 'Invalid') {
+    modeprm = {invalid: null};
+  }
+
   await usingApi(async (api) => {
     // Get number of collections before the transaction
     const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());
 
     // Run the CreateCollection transaction
     const alicePrivateKey = privateKey('//Alice');
-    const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), mode);
+    const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), modeprm);
     const events = await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;
     const result = getCreateCollectionResult(events);
 
@@ -156,11 +235,12 @@
     const BcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());
 
     // What to expect
+    // tslint:disable-next-line:no-unused-expression
     expect(result.success).to.be.false;
     expect(BcollectionCount).to.be.equal(AcollectionCount, 'Error: Collection with incorrect data created.');
   });
 }
-  
+
 export async function findUnusedAddress(api: ApiPromise): Promise<IKeyringPair> {
   let bal = new BigNumber(0);
   let unused;
@@ -170,7 +250,7 @@
     unused = keyring.addFromUri(`//${randomSeed}`);
     bal = new BigNumber((await api.query.system.account(unused.address)).data.free.toString());
   } while (bal.toFixed() != '0');
-  return unused; 
+  return unused;
 }
 
 function getDestroyResult(events: EventRecord[]): boolean {
@@ -201,7 +281,7 @@
     const events = await submitTransactionAsync(alicePrivateKey, tx);
     const result = getDestroyResult(events);
 
-    // Get the collection 
+    // Get the collection
     const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
 
     // What to expect
@@ -220,7 +300,7 @@
     const events = await submitTransactionAsync(alicePrivateKey, tx);
     const result = getGenericResult(events);
 
-    // Get the collection 
+    // Get the collection
     const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
 
     // What to expect
@@ -239,7 +319,7 @@
     const events = await submitTransactionAsync(alicePrivateKey, tx);
     const result = getGenericResult(events);
 
-    // Get the collection 
+    // Get the collection
     const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
 
     // What to expect
@@ -278,7 +358,7 @@
     const events = await submitTransactionAsync(sender, tx);
     const result = getGenericResult(events);
 
-    // Get the collection 
+    // Get the collection
     const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
 
     // What to expect
@@ -300,49 +380,132 @@
 
 export interface CreateFungibleData extends Struct {
   readonly value: u128;
-};
+}
 
-export interface CreateReFungibleData extends Struct {};
-export interface CreateNftData extends Struct {};
+export interface CreateReFungibleData extends Struct {}
+export interface CreateNftData extends Struct {}
 
 export interface CreateItemData extends Enum {
-  NFT: CreateNftData,
-  Fungible: CreateFungibleData,
-  ReFungible: CreateReFungibleData
-};
+  NFT: CreateNftData;
+  Fungible: CreateFungibleData;
+  ReFungible: CreateReFungibleData;
+}
+
+export async function
+approveExpectSuccess(collectionId: number,
+                     tokenId: number, owner: IKeyringPair, approved: IKeyringPair, amount: number = 1) {
+  await usingApi(async (api: ApiPromise) => {
+    const allowanceBefore =
+      await api.query.nft.allowances(collectionId, [tokenId, owner.address, approved.address]) as unknown as BN;
+    const approveNftTx = await api.tx.nft.approve(approved.address, collectionId, tokenId, amount);
+    const events = await submitTransactionAsync(owner, approveNftTx);
+    const result = getCreateItemResult(events);
+    // tslint:disable-next-line:no-unused-expression
+    expect(result.success).to.be.true;
+    const allowanceAfter =
+      await api.query.nft.allowances(collectionId, [tokenId, owner.address, approved.address]) as unknown as BN;
+    expect(allowanceAfter.toNumber() - allowanceBefore.toNumber()).to.be.equal(amount);
+  });
+}
 
+export async function
+transferFromExpectSuccess(collectionId: number,
+                          tokenId: number,
+                          accountApproved: IKeyringPair,
+                          accountFrom: IKeyringPair,
+                          accountTo: IKeyringPair,
+                          value: number = 1,
+                          type: string = 'NFT') {
+  await usingApi(async (api: ApiPromise) => {
+    let balanceBefore = new BN(0);
+    if (type === 'Fungible') {
+      balanceBefore = await api.query.nft.balance(collectionId, accountTo.address) as unknown as BN;
+    }
+    const transferFromTx = await api.tx.nft.transferFrom(
+      accountFrom.address, accountTo.address, collectionId, tokenId, value);
+    const events = await submitTransactionAsync(accountFrom, transferFromTx);
+    const result = getCreateItemResult(events);
+    // tslint:disable-next-line:no-unused-expression
+    expect(result.success).to.be.true;
+    if (type === 'NFT') {
+      const nftItemData = await api.query.nft.nftItemList(collectionId, tokenId) as unknown as ITokenDataType;
+      expect(nftItemData.Owner.toString()).to.be.equal(accountTo.address);
+    }
+    if (type === 'Fungible') {
+      const balanceAfter = await api.query.nft.balance(collectionId, accountTo.address) as unknown as BN;
+      expect(balanceAfter.sub(balanceBefore).toNumber()).to.be.equal(value);
+    }
+    if (type === 'ReFungible') {
+      const nftItemData =
+        await api.query.nft.reFungibleItemList(collectionId, tokenId) as unknown as IReFungibleTokenDataType;
+      expect(nftItemData.Owner[0].Owner.toString()).to.be.equal(accountTo.address);
+      expect(nftItemData.Owner[0].Fraction.toNumber()).to.be.equal(value);
+    }
+  });
+}
+
+export async function
+transferFromExpectFail(collectionId: number,
+                       tokenId: number,
+                       accountApproved: IKeyringPair,
+                       accountFrom: IKeyringPair,
+                       accountTo: IKeyringPair,
+                       value: number = 1) {
+  await usingApi(async (api: ApiPromise) => {
+    const transferFromTx = await api.tx.nft.transferFrom(
+      accountFrom.address, accountTo.address, collectionId, tokenId, value);
+    const events = await expect(submitTransactionExpectFailAsync(accountApproved, transferFromTx)).to.be.rejected;
+    const result = getCreateCollectionResult(events);
+    // tslint:disable-next-line:no-unused-expression
+    expect(result.success).to.be.false;
+  });
+}
+
+export async function
+approveExpectFail(collectionId: number,
+                  tokenId: number, owner: IKeyringPair, approved: IKeyringPair, amount: number = 1) {
+  await usingApi(async (api: ApiPromise) => {
+    const approveNftTx = await api.tx.nft.approve(approved.address, collectionId, tokenId, amount);
+    const events = await expect(submitTransactionExpectFailAsync(owner, approveNftTx)).to.be.rejected;
+    const result = getCreateCollectionResult(events);
+    // tslint:disable-next-line:no-unused-expression
+    expect(result.success).to.be.false;
+  });
+}
+
 export async function createItemExpectSuccess(sender: IKeyringPair, collectionId: number, createMode: string, owner: string = '') {
   let newItemId: number = 0;
   await usingApi(async (api) => {
-    const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());
-    const Aitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON();    
+    const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString(), 10);
+    const Aitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON();
     const AItemBalance = new BigNumber(Aitem.Value);
 
-    if (owner === '') owner = sender.address;
+    if (owner === '') {
+      owner = sender.address;
+    }
 
     let tx;
-    if (createMode == 'Fungible') {
-      let createData = {fungible: {value: 10}};
+    if (createMode === 'Fungible') {
+      const createData = {fungible: {value: 10}};
       tx = api.tx.nft.createItem(collectionId, owner, createData);
-    }
-    else {
+    } else {
       tx = api.tx.nft.createItem(collectionId, owner, createMode);
     }
     const events = await submitTransactionAsync(sender, tx);
     const result = getCreateItemResult(events);
 
-    const BItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());
-    const Bitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON();    
+    const BItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString(), 10);
+    const Bitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON();
     const BItemBalance = new BigNumber(Bitem.Value);
 
     // What to expect
+    // tslint:disable-next-line:no-unused-expression
     expect(result.success).to.be.true;
-    if (createMode == 'Fungible') {
+    if (createMode === 'Fungible') {
       expect(BItemBalance.minus(AItemBalance).toNumber()).to.be.equal(10);
+    } else {
+      expect(BItemCount).to.be.equal(AItemCount + 1);
     }
-    else {
-      expect(BItemCount).to.be.equal(AItemCount+1);
-    }
     expect(collectionId).to.be.equal(result.collectionId);
     expect(BItemCount).to.be.equal(result.itemId);
     newItemId = result.itemId;
@@ -358,7 +521,7 @@
     const events = await submitTransactionAsync(sender, tx);
     const result = getGenericResult(events);
 
-    // Get the collection 
+    // Get the collection
     const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
 
     // What to expect
@@ -375,7 +538,7 @@
     const events = await submitTransactionAsync(sender, tx);
     const result = getGenericResult(events);
 
-    // Get the collection 
+    // Get the collection
     const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
 
     // What to expect
@@ -392,7 +555,7 @@
     const events = await submitTransactionAsync(sender, tx);
     const result = getGenericResult(events);
 
-    // Get the collection 
+    // Get the collection
     const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
 
     // What to expect