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

difftreelog

Merge branch 'develop' into feature/NFTPAR-142

sotmorskiy2020-12-02parents: #9774e79 #c88ca14.patch.diff
in: master
# Conflicts:
#	pallets/nft/src/lib.rs

5 files changed

modifiedpallets/nft/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/nft/src/benchmarking.rs
+++ b/pallets/nft/src/benchmarking.rs
@@ -165,7 +165,7 @@
             let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
             let mode: CollectionMode = CollectionMode::NFT;
             let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
-            let nft_data = CreateNftData {
+            let mut nft_data = CreateNftData {
                 const_data: vec![],
                 variable_data: vec![]
             };
@@ -173,7 +173,7 @@
                 nft_data.const_data.push(10);
                 nft_data.variable_data.push(10);
             }
-            let mut data = CreateItemData::NFT(nft_data);
+            let data = CreateItemData::NFT(nft_data);
             Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
 
         }: create_item(RawOrigin::Signed(caller.clone()), 2, caller.clone(), data)
modifiedpallets/nft/src/default_weights.rsdiffbeforeafterboth
--- a/pallets/nft/src/default_weights.rs
+++ b/pallets/nft/src/default_weights.rs
@@ -63,7 +63,7 @@
     }  
     fn create_item(s: usize, ) -> Weight {
         (130_000_000 as Weight)
-            .saturating_add((2135 as Weight).saturating_mul(s as Weight))
+            .saturating_add((2135 as Weight).saturating_mul(s as Weight).saturating_mul(500 as Weight)) // 500 is temporary multiplier, fee for storage
             .saturating_add(DbWeight::get().reads(10 as Weight))
             .saturating_add(DbWeight::get().writes(8 as Weight))
     }  
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
before · pallets/nft/src/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]23#[cfg(feature = "std")]4pub use std::*;56#[cfg(feature = "std")]7pub use serde::*;89use codec::{Decode, Encode};10pub use frame_support::{11    construct_runtime, decl_event, decl_module, decl_storage,12    dispatch::DispatchResult,13    ensure, parameter_types, fail,14    traits::{15        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,16        Randomness, WithdrawReason,17    },18    weights::{19        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},20        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,21        WeightToFeePolynomial,22    },23    IsSubType, StorageValue,24};25// use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight};2627use frame_system::{self as system, ensure_signed, ensure_root};28use sp_runtime::sp_std::prelude::Vec;29use sp_runtime::{30    traits::{31        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SignedExtension, Zero,32    },33    transaction_validity::{34        InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,35    },36    FixedPointOperand, FixedU128,37};38use pallet_contracts::ContractAddressFor;39use sp_runtime::traits::StaticLookup;4041#[cfg(test)]42mod mock;4344#[cfg(test)]45mod tests;4647mod default_weights;4849// Structs50// #region5152#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]53#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]54pub enum CollectionMode {55    Invalid,56    NFT,57    // decimal points58    Fungible(u32),59    // decimal points60    ReFungible(u32),61}6263impl Into<u8> for CollectionMode {64    fn into(self) -> u8 {65        match self {66            CollectionMode::Invalid => 0,67            CollectionMode::NFT => 1,68            CollectionMode::Fungible(_) => 2,69            CollectionMode::ReFungible(_) => 3,70        }71    }72}7374#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]75#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]76pub enum AccessMode {77    Normal,78    WhiteList,79}80impl Default for AccessMode {81    fn default() -> Self {82        Self::Normal83    }84}8586impl Default for CollectionMode {87    fn default() -> Self {88        Self::Invalid89    }90}9192#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]93#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]94pub struct Ownership<AccountId> {95    pub owner: AccountId,96    pub fraction: u128,97}9899#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]100#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]101pub struct CollectionType<AccountId> {102    pub owner: AccountId,103    pub mode: CollectionMode,104    pub access: AccessMode,105    pub decimal_points: u32,106    pub name: Vec<u16>,        // 64 include null escape char107    pub description: Vec<u16>, // 256 include null escape char108    pub token_prefix: Vec<u8>, // 16 include null escape char109    pub mint_mode: bool,110    pub offchain_schema: Vec<u8>,111    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender112    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship113    pub variable_on_chain_schema: Vec<u8>, //114    pub const_on_chain_schema: Vec<u8>, //115}116117#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]118#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]119pub struct CollectionAdminsType<AccountId> {120    pub admin: AccountId,121    pub collection_id: u64,122}123124#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]125#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]126pub struct NftItemType<AccountId> {127    pub collection: u64,128    pub owner: AccountId,129    pub const_data: Vec<u8>,130    pub variable_data: Vec<u8>,131}132133#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]134#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]135pub struct FungibleItemType<AccountId> {136    pub collection: u64,137    pub owner: AccountId,138    pub value: u128,139}140141#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]142#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]143pub struct ReFungibleItemType<AccountId> {144    pub collection: u64,145    pub owner: Vec<Ownership<AccountId>>,146    pub const_data: Vec<u8>,147    pub variable_data: Vec<u8>,148}149150#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]151#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]152pub struct ApprovePermissions<AccountId> {153    pub approved: AccountId,154    pub amount: u64,155}156157#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]158#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]159pub struct VestingItem<AccountId, Moment> {160    pub sender: AccountId,161    pub recipient: AccountId,162    pub collection_id: u64,163    pub item_id: u64,164    pub amount: u64,165    pub vesting_date: Moment,166}167168#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]169#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170pub struct BasketItem<AccountId, BlockNumber> {171    pub address: AccountId,172    pub start_block: BlockNumber,173}174175#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]176#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]177pub struct ChainLimits {178    pub collection_numbers_limit: u64,179    pub account_token_ownership_limit: u64,180    pub collections_admins_limit: u64,181    pub custom_data_limit: u32,182183    // Timeouts for item types in passed blocks184    pub nft_sponsor_transfer_timeout: u32,185    pub fungible_sponsor_transfer_timeout: u32,186    pub refungible_sponsor_transfer_timeout: u32,187}188189pub trait WeightInfo {190	fn create_collection() -> Weight;191	fn destroy_collection() -> Weight;192	fn add_to_white_list() -> Weight;193	fn remove_from_white_list() -> Weight;194    fn set_public_access_mode() -> Weight;195    fn set_mint_permission() -> Weight;196    fn change_collection_owner() -> Weight;197    fn add_collection_admin() -> Weight;198    fn remove_collection_admin() -> Weight;199    fn set_collection_sponsor() -> Weight;200    fn confirm_sponsorship() -> Weight;201    fn remove_collection_sponsor() -> Weight;202    fn create_item(s: usize) -> Weight;203    fn burn_item() -> Weight;204    fn transfer() -> Weight;205    fn approve() -> Weight;206    fn transfer_from() -> Weight;207    fn set_offchain_schema() -> Weight;208    fn set_const_on_chain_schema() -> Weight;209    fn set_variable_on_chain_schema() -> Weight;210    fn set_variable_meta_data() -> Weight;211    // fn enable_contract_sponsoring() -> Weight;212}213214#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]215#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]216pub struct CreateNftData {217    pub const_data: Vec<u8>,218    pub variable_data: Vec<u8>,219}220221#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]222#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]223pub struct CreateFungibleData {224}225226#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]227#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]228pub struct CreateReFungibleData {229    pub const_data: Vec<u8>,230    pub variable_data: Vec<u8>,231}232233#[derive(Encode, Decode, Debug, Clone, PartialEq)]234#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]235pub enum CreateItemData {236    NFT(CreateNftData),237    Fungible(CreateFungibleData),238    ReFungible(CreateReFungibleData)239}240241impl CreateItemData {242    pub fn len(&self) -> usize {243        let len = match self {244            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),245            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),246            _ => 0247        };248        249        return len;250    }251}252253impl From<CreateNftData> for CreateItemData {254    fn from(item: CreateNftData) -> Self {255        CreateItemData::NFT(item)256    }257}258259impl From<CreateReFungibleData> for CreateItemData {260    fn from(item: CreateReFungibleData) -> Self {261        CreateItemData::ReFungible(item)262    }263}264265impl From<CreateFungibleData> for CreateItemData {266    fn from(item: CreateFungibleData) -> Self {267        CreateItemData::Fungible(item)268    }269}270271pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {272    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;273274    /// Weight information for extrinsics in this pallet.275	type WeightInfo: WeightInfo;276}277278#[cfg(feature = "runtime-benchmarks")]279mod benchmarking;280281// #endregion282283decl_storage! {284    trait Store for Module<T: Trait> as Nft {285286        // Private members287        NextCollectionID: u64;288        CreatedCollectionCount: u64;289        ChainVersion: u64;290        ItemListIndex: map hasher(blake2_128_concat) u64 => u64;291292        // Chain limits struct293        pub ChainLimit get(fn chain_limit) config(): ChainLimits;294295        // Bound counters296        CollectionCount: u64;297        pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;298299        // Basic collections300        pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;301        pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;302        pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;303304        /// Balance owner per collection map305        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;306307        /// second parameter: item id + owner account id308        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;309310        /// Item collections311        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;312        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;313        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;314315        /// Index list316        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;317318        /// Tokens transfer baskets319        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;320        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => Vec<BasketItem<T::AccountId, T::BlockNumber>>;321        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;322323        // Contract Sponsorship and Ownership324        pub ContractOwner get(fn contract_owner): map hasher(identity) T::AccountId => T::AccountId;325        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(identity) T::AccountId => bool;326    }327    add_extra_genesis {328        build(|config: &GenesisConfig<T>| {329            // Modification of storage330            for (_num, _c) in &config.collection {331                <Module<T>>::init_collection(_c);332            }333334            for (_num, _q, _i) in &config.nft_item_id {335                <Module<T>>::init_nft_token(_i);336            }337338            for (_num, _q, _i) in &config.fungible_item_id {339                <Module<T>>::init_fungible_token(_i);340            }341342            for (_num, _q, _i) in &config.refungible_item_id {343                <Module<T>>::init_refungible_token(_i);344            }345        })346    }347}348349decl_event!(350    pub enum Event<T>351    where352        AccountId = <T as system::Trait>::AccountId,353    {354        /// New collection was created355        /// 356        /// # Arguments357        /// 358        /// * collection_id: Globally unique identifier of newly created collection.359        /// 360        /// * mode: [CollectionMode] converted into u8.361        /// 362        /// * account_id: Collection owner.363        Created(u64, u8, AccountId),364365        /// New item was created.366        /// 367        /// # Arguments368        /// 369        /// * collection_id: Id of the collection where item was created.370        /// 371        /// * item_id: Id of an item. Unique within the collection.372        ItemCreated(u64, u64),373374        /// Collection item was burned.375        /// 376        /// # Arguments377        /// 378        /// collection_id.379        /// 380        /// item_id: Identifier of burned NFT.381        ItemDestroyed(u64, u64),382    }383);384385decl_module! {386    pub struct Module<T: Trait> for enum Call where origin: T::Origin {387388        fn deposit_event() = default;389390        fn on_initialize(now: T::BlockNumber) -> Weight {391392            if ChainVersion::get() < 2393            {394                let value = NextCollectionID::get();395                CreatedCollectionCount::put(value);396                ChainVersion::put(2);397            }398399            0400        }401402        /// 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.403        /// 404        /// # Permissions405        /// 406        /// * Anyone.407        /// 408        /// # Arguments409        /// 410        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.411        /// 412        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.413        /// 414        /// * token_prefix: UTF-8 string with token prefix.415        /// 416        /// * mode: [CollectionMode] collection type and type dependent data.417        // returns collection ID418        #[weight = T::WeightInfo::create_collection()]419        pub fn create_collection(origin,420                                 collection_name: Vec<u16>,421                                 collection_description: Vec<u16>,422                                 token_prefix: Vec<u8>,423                                 mode: CollectionMode) -> DispatchResult {424425            // Anyone can create a collection426            let who = ensure_signed(origin)?;427428            let decimal_points = match mode {429                CollectionMode::Fungible(points) => points,430                CollectionMode::ReFungible(points) => points,431                _ => 0432            };433434            // bound Total number of collections435            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");436437            // check params438            ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");439440            let mut name = collection_name.to_vec();441            name.push(0);442            ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");443444            let mut description = collection_description.to_vec();445            description.push(0);446            ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");447448            let mut prefix = token_prefix.to_vec();449            prefix.push(0);450            ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");451452            // Generate next collection ID453            let next_id = CreatedCollectionCount::get()454                .checked_add(1)455                .expect("collection id error");456457            // bound counter458            let total = CollectionCount::get()459                .checked_add(1)460                .expect("collection counter error");461462            CreatedCollectionCount::put(next_id);463            CollectionCount::put(total);464465            // Create new collection466            let new_collection = CollectionType {467                owner: who.clone(),468                name: name,469                mode: mode.clone(),470                mint_mode: false,471                access: AccessMode::Normal,472                description: description,473                decimal_points: decimal_points,474                token_prefix: prefix,475                offchain_schema: Vec::new(),476                sponsor: T::AccountId::default(),477                unconfirmed_sponsor: T::AccountId::default(),478                variable_on_chain_schema: Vec::new(),479                const_on_chain_schema: Vec::new(),480            };481482            // Add new collection to map483            <Collection<T>>::insert(next_id, new_collection);484485            // call event486            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));487488            Ok(())489        }490491        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.492        ///     493        /// # Permissions494        /// 495        /// * Collection Owner.496        /// 497        /// # Arguments498        /// 499        /// * collection_id: collection to destroy.500        #[weight = T::WeightInfo::destroy_collection()]501        pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {502503            let sender = ensure_signed(origin)?;504            Self::check_owner_permissions(collection_id, sender)?;505506            <AddressTokens<T>>::remove_prefix(collection_id);507            <ApprovedList<T>>::remove_prefix(collection_id);508            <Balance<T>>::remove_prefix(collection_id);509            <ItemListIndex>::remove(collection_id);510            <AdminList<T>>::remove(collection_id);511            <Collection<T>>::remove(collection_id);512            <WhiteList<T>>::remove(collection_id);513514            <NftItemList<T>>::remove_prefix(collection_id);515            <FungibleItemList<T>>::remove_prefix(collection_id);516            <ReFungibleItemList<T>>::remove_prefix(collection_id);517518            <NftTransferBasket<T>>::remove_prefix(collection_id);519            <FungibleTransferBasket<T>>::remove_prefix(collection_id);520            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);521522            if CollectionCount::get() > 0523            {524                // bound couter525                let total = CollectionCount::get()526                    .checked_sub(1)527                    .expect("collection counter error");528529                CollectionCount::put(total);530            }531532            Ok(())533        }534535        /// Add an address to white list.536        /// 537        /// # Permissions538        /// 539        /// * Collection Owner540        /// * Collection Admin541        /// 542        /// # Arguments543        /// 544        /// * collection_id.545        /// 546        /// * address.547        #[weight = T::WeightInfo::add_to_white_list()]548        pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{549550            let sender = ensure_signed(origin)?;551            Self::check_owner_or_admin_permissions(collection_id, sender)?;552553            let mut white_list_collection: Vec<T::AccountId>;554            if <WhiteList<T>>::contains_key(collection_id) {555                white_list_collection = <WhiteList<T>>::get(collection_id);556                if !white_list_collection.contains(&address.clone())557                {558                    white_list_collection.push(address.clone());559                }560            }561            else {562                white_list_collection = Vec::new();563                white_list_collection.push(address.clone());564            }565566            <WhiteList<T>>::insert(collection_id, white_list_collection);567            Ok(())568        }569570        /// Remove an address from white list.571        /// 572        /// # Permissions573        /// 574        /// * Collection Owner575        /// * Collection Admin576        /// 577        /// # Arguments578        /// 579        /// * collection_id.580        /// 581        /// * address.582        #[weight = T::WeightInfo::remove_from_white_list()]583        pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{584585            let sender = ensure_signed(origin)?;586            Self::check_owner_or_admin_permissions(collection_id, sender)?;587588            if <WhiteList<T>>::contains_key(collection_id) {589                let mut white_list_collection = <WhiteList<T>>::get(collection_id);590                if white_list_collection.contains(&address.clone())591                {592                    white_list_collection.retain(|i| *i != address.clone());593                    <WhiteList<T>>::insert(collection_id, white_list_collection);594                }595            }596597            Ok(())598        }599600        /// Toggle between normal and white list access for the methods with access for `Anyone`.601        /// 602        /// # Permissions603        /// 604        /// * Collection Owner.605        /// 606        /// # Arguments607        /// 608        /// * collection_id.609        /// 610        /// * mode: [AccessMode]611        #[weight = T::WeightInfo::set_public_access_mode()]612        pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult613        {614            let sender = ensure_signed(origin)?;615616            Self::check_owner_permissions(collection_id, sender)?;617            let mut target_collection = <Collection<T>>::get(collection_id);618            target_collection.access = mode;619            <Collection<T>>::insert(collection_id, target_collection);620621            Ok(())622        }623624        /// Allows Anyone to create tokens if:625        /// * White List is enabled, and626        /// * Address is added to white list, and627        /// * This method was called with True parameter628        /// 629        /// # Permissions630        /// * Collection Owner631        ///632        /// # Arguments633        /// 634        /// * collection_id.635        /// 636        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.637        #[weight = T::WeightInfo::set_mint_permission()]638        pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult639        {640            let sender = ensure_signed(origin)?;641642            Self::check_owner_permissions(collection_id, sender)?;643            let mut target_collection = <Collection<T>>::get(collection_id);644            target_collection.mint_mode = mint_permission;645            <Collection<T>>::insert(collection_id, target_collection);646647            Ok(())648        }649650        /// Change the owner of the collection.651        /// 652        /// # Permissions653        /// 654        /// * Collection Owner.655        /// 656        /// # Arguments657        /// 658        /// * collection_id.659        /// 660        /// * new_owner.661        #[weight = T::WeightInfo::change_collection_owner()]662        pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {663664            let sender = ensure_signed(origin)?;665            Self::check_owner_permissions(collection_id, sender)?;666            let mut target_collection = <Collection<T>>::get(collection_id);667            target_collection.owner = new_owner;668            <Collection<T>>::insert(collection_id, target_collection);669670            Ok(())671        }672673        /// Adds an admin of the Collection.674        /// 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. 675        /// 676        /// # Permissions677        /// 678        /// * Collection Owner.679        /// * Collection Admin.680        /// 681        /// # Arguments682        /// 683        /// * collection_id: ID of the Collection to add admin for.684        /// 685        /// * new_admin_id: Address of new admin to add.686        #[weight = T::WeightInfo::add_collection_admin()]687        pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {688689            let sender = ensure_signed(origin)?;690            Self::check_owner_or_admin_permissions(collection_id, sender)?;691            let mut admin_arr: Vec<T::AccountId> = Vec::new();692693            if <AdminList<T>>::contains_key(collection_id)694            {695                admin_arr = <AdminList<T>>::get(collection_id);696                ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");697            }698699            // Number of collection admins700            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");701702            admin_arr.push(new_admin_id);703            <AdminList<T>>::insert(collection_id, admin_arr);704705            Ok(())706        }707708        /// 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.709        ///710        /// # Permissions711        /// 712        /// * Collection Owner.713        /// * Collection Admin.714        /// 715        /// # Arguments716        /// 717        /// * collection_id: ID of the Collection to remove admin for.718        /// 719        /// * account_id: Address of admin to remove.720        #[weight = T::WeightInfo::remove_collection_admin()]721        pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {722723            let sender = ensure_signed(origin)?;724            Self::check_owner_or_admin_permissions(collection_id, sender)?;725726            if <AdminList<T>>::contains_key(collection_id)727            {728                let mut admin_arr = <AdminList<T>>::get(collection_id);729                admin_arr.retain(|i| *i != account_id);730                <AdminList<T>>::insert(collection_id, admin_arr);731            }732733            Ok(())734        }735736        /// # Permissions737        /// 738        /// * Collection Owner739        /// 740        /// # Arguments741        /// 742        /// * collection_id.743        /// 744        /// * new_sponsor.745        #[weight = T::WeightInfo::set_collection_sponsor()]746        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {747748            let sender = ensure_signed(origin)?;749            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");750751            let mut target_collection = <Collection<T>>::get(collection_id);752            ensure!(sender == target_collection.owner, "You do not own this collection");753754            target_collection.unconfirmed_sponsor = new_sponsor;755            <Collection<T>>::insert(collection_id, target_collection);756757            Ok(())758        }759760        /// # Permissions761        /// 762        /// * Sponsor.763        /// 764        /// # Arguments765        /// 766        /// * collection_id.767        #[weight = T::WeightInfo::confirm_sponsorship()]768        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {769770            let sender = ensure_signed(origin)?;771            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");772773            let mut target_collection = <Collection<T>>::get(collection_id);774            ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");775776            target_collection.sponsor = target_collection.unconfirmed_sponsor;777            target_collection.unconfirmed_sponsor = T::AccountId::default();778            <Collection<T>>::insert(collection_id, target_collection);779780            Ok(())781        }782783        /// Switch back to pay-per-own-transaction model.784        ///785        /// # Permissions786        ///787        /// * Collection owner.788        /// 789        /// # Arguments790        /// 791        /// * collection_id.792        #[weight = T::WeightInfo::remove_collection_sponsor()]793        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {794795            let sender = ensure_signed(origin)?;796            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");797798            let mut target_collection = <Collection<T>>::get(collection_id);799            ensure!(sender == target_collection.owner, "You do not own this collection");800801            target_collection.sponsor = T::AccountId::default();802            <Collection<T>>::insert(collection_id, target_collection);803804            Ok(())805        }806807        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.808        /// 809        /// # Permissions810        /// 811        /// * Collection Owner.812        /// * Collection Admin.813        /// * Anyone if814        ///     * White List is enabled, and815        ///     * Address is added to white list, and816        ///     * MintPermission is enabled (see SetMintPermission method)817        /// 818        /// # Arguments819        /// 820        /// * collection_id: ID of the collection.821        /// 822        /// * owner: Address, initial owner of the NFT.823        ///824        /// * data: Token data to store on chain.825        // #[weight =826        // (130_000_000 as Weight)827        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))828        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))829        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]830831        #[weight = T::WeightInfo::create_item(data.len())]832        pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {833834            let sender = ensure_signed(origin)?;835            Self::collection_exists(collection_id)?;836            let target_collection = <Collection<T>>::get(collection_id);837838            if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {839                ensure!(target_collection.mint_mode == true, "Public minting is not allowed for this collection.");840                Self::check_white_list(collection_id, &owner)?;841                Self::check_white_list(collection_id, &sender)?;842            }843844            match target_collection.mode845            {846                CollectionMode::NFT => {847                    if let CreateItemData::NFT(data) = data {848                        // check sizes849                        ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");850                        ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");851    852                        // Create nft item853                        let item = NftItemType {854                            collection: collection_id,855                            owner: owner,856                            const_data: data.const_data.clone(),857                            variable_data: data.variable_data.clone() 858                        };859    860                        Self::add_nft_item(item)?;861                    862                    } else {863                        fail!("Not NFT item data used to mint in NFT collection.");864                    }865                },866                CollectionMode::Fungible(_) => {867                    if let CreateItemData::Fungible(_) = data {868    869                        let item = FungibleItemType {870                            collection: collection_id,871                            owner: owner,872                            value: (10 as u128).pow(target_collection.decimal_points)873                        };874    875                        Self::add_fungible_item(item)?;876                    } else {877                        fail!("Not Fungible item data used to mint in Fungible collection.");878                    }879                },880                CollectionMode::ReFungible(_) => {881                    if let CreateItemData::ReFungible(data) = data {882    883                        // check sizes884                        ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");885                        ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");886    887                        let mut owner_list = Vec::new();888                        let value = (10 as u128).pow(target_collection.decimal_points);889                        owner_list.push(Ownership {owner: owner.clone(), fraction: value});890    891                        let item = ReFungibleItemType {892                            collection: collection_id,893                            owner: owner_list,894                            const_data: data.const_data.clone(),895                            variable_data: data.variable_data.clone() 896                        };897    898                        Self::add_refungible_item(item)?;899                    } else {900                        fail!("Not Re Fungible item data used to mint in Re Fungible collection.");901                    }902                },903                _ => { ensure!(1 == 0,"Unexpected collection type."); }904            };905906            // call event907            Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));908909            Ok(())910        }911912        /// Destroys a concrete instance of NFT.913        /// 914        /// # Permissions915        /// 916        /// * Collection Owner.917        /// * Collection Admin.918        /// * Current NFT Owner.919        /// 920        /// # Arguments921        /// 922        /// * collection_id: ID of the collection.923        /// 924        /// * item_id: ID of NFT to burn.925        #[weight = T::WeightInfo::burn_item()]926        pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {927928            let sender = ensure_signed(origin)?;929            Self::collection_exists(collection_id)?;930931            // Transfer permissions check932            let target_collection = <Collection<T>>::get(collection_id);933            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||934                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),935                "Only item owner, collection owner and admins can modify item");936937            if target_collection.access == AccessMode::WhiteList {938                Self::check_white_list(collection_id, &sender)?;939            }940941            match target_collection.mode942            {943                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,944                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,945                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,946                _ => ()947            };948949            // call event950            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));951952            Ok(())953        }954955        /// Change ownership of the token.956        /// 957        /// # Permissions958        /// 959        /// * Collection Owner960        /// * Collection Admin961        /// * Current NFT owner962        ///963        /// # Arguments964        /// 965        /// * recipient: Address of token recipient.966        /// 967        /// * collection_id.968        /// 969        /// * item_id: ID of the item970        ///     * Non-Fungible Mode: Required.971        ///     * Fungible Mode: Ignored.972        ///     * Re-Fungible Mode: Required.973        /// 974        /// * value: Amount to transfer.975        ///     * Non-Fungible Mode: Ignored976        ///     * Fungible Mode: Must specify transferred amount977        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)978        #[weight = T::WeightInfo::transfer()]979        pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {980981            let sender = ensure_signed(origin)?;982983            // Transfer permissions check984            let target_collection = <Collection<T>>::get(collection_id);985            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||986                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),987                "Only item owner, collection owner and admins can modify item");988989            if target_collection.access == AccessMode::WhiteList {990                Self::check_white_list(collection_id, &sender)?;991                Self::check_white_list(collection_id, &recipient)?;992            }993994            match target_collection.mode995            {996                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,997                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,998                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,999                _ => ()1000            };10011002            Ok(())1003        }10041005        /// Set, change, or remove approved address to transfer the ownership of the NFT.1006        /// 1007        /// # Permissions1008        /// 1009        /// * Collection Owner1010        /// * Collection Admin1011        /// * Current NFT owner1012        /// 1013        /// # Arguments1014        /// 1015        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1016        /// 1017        /// * collection_id.1018        /// 1019        /// * item_id: ID of the item.1020        #[weight = T::WeightInfo::approve()]1021        pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {10221023            let sender = ensure_signed(origin)?;10241025            // Transfer permissions check1026            let target_collection = <Collection<T>>::get(collection_id);1027            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1028                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1029                "Only item owner, collection owner and admins can approve");10301031            if target_collection.access == AccessMode::WhiteList {1032                Self::check_white_list(collection_id, &sender)?;1033                Self::check_white_list(collection_id, &approved)?;1034            }10351036            // amount param stub1037            let amount = 100000000;10381039            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1040            if list_exists {10411042                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1043                let item_contains = list.iter().any(|i| i.approved == approved);10441045                if !item_contains {1046                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1047                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1048                }1049            } else {10501051                let mut list = Vec::new();1052                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1053                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1054            }10551056            Ok(())1057        }1058        1059        /// 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.1060        /// 1061        /// # Permissions1062        /// * Collection Owner1063        /// * Collection Admin1064        /// * Current NFT owner1065        /// * Address approved by current NFT owner1066        /// 1067        /// # Arguments1068        /// 1069        /// * from: Address that owns token.1070        /// 1071        /// * recipient: Address of token recipient.1072        /// 1073        /// * collection_id.1074        /// 1075        /// * item_id: ID of the item.1076        /// 1077        /// * value: Amount to transfer.1078        #[weight = T::WeightInfo::transfer_from()]1079        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10801081            let sender = ensure_signed(origin)?;1082            let mut appoved_transfer = false;10831084            // Check approve1085            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1086                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1087                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1088                if opt_item.is_some()1089                {1090                    appoved_transfer = true;1091                    ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1092                }1093            }10941095            // Transfer permissions check1096            let target_collection = <Collection<T>>::get(collection_id);1097            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1098                "Only item owner, collection owner and admins can modify items");10991100            if target_collection.access == AccessMode::WhiteList {1101                Self::check_white_list(collection_id, &sender)?;1102                Self::check_white_list(collection_id, &recipient)?;1103            }11041105            // remove approve1106            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1107                .into_iter().filter(|i| i.approved != sender.clone()).collect();1108            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);110911101111            match target_collection.mode1112            {1113                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1114                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1115                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1116                _ => ()1117            };11181119            Ok(())1120        }11211122        ///1123        #[weight = 0]1124        pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11251126            // let no_perm_mes = "You do not have permissions to modify this collection";1127            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1128            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1129            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11301131            // // on_nft_received  call11321133            // Self::transfer(origin, collection_id, item_id, new_owner)?;11341135            Ok(())1136        }1137        1138        /// Set off-chain data schema.1139        /// 1140        /// # Permissions1141        /// 1142        /// * Collection Owner1143        /// * Collection Admin1144        /// 1145        /// # Arguments1146        /// 1147        /// * collection_id.1148        /// 1149        /// * schema: String representing the offchain data schema.1150        #[weight = T::WeightInfo::set_variable_meta_data()]1151        pub fn set_variable_meta_data (1152            origin,1153            collection_id: u64,1154            item_id: u64,1155            data: Vec<u8>1156        ) -> DispatchResult {1157            let sender = ensure_signed(origin)?;1158            1159            Self::collection_exists(collection_id)?;11601161            // Modify permissions check1162            let target_collection = <Collection<T>>::get(collection_id);1163            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1164                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1165                "Only item owner, collection owner and admins can modify item.");11661167            Self::item_exists(collection_id, item_id, &target_collection.mode)?;11681169            match target_collection.mode1170            {1171                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1172                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1173                CollectionMode::Fungible(_) => fail!("Can't store metadata in fungible tokens."),1174                _ => fail!("Unexpected collection type.")1175            };11761177            Ok(())1178        }1179        11801181        /// Set off-chain data schema.1182        /// 1183        /// # Permissions1184        /// 1185        /// * Collection Owner1186        /// * Collection Admin1187        /// 1188        /// # Arguments1189        /// 1190        /// * collection_id.1191        /// 1192        /// * schema: String representing the offchain data schema.1193        #[weight = T::WeightInfo::set_offchain_schema()]1194        pub fn set_offchain_schema(1195            origin,1196            collection_id: u64,1197            schema: Vec<u8>1198        ) -> DispatchResult {1199            let sender = ensure_signed(origin)?;1200            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12011202            let mut target_collection = <Collection<T>>::get(collection_id);1203            target_collection.offchain_schema = schema;1204            <Collection<T>>::insert(collection_id, target_collection);12051206            Ok(())1207        }12081209        /// Set const on-chain data schema.1210        /// 1211        /// # Permissions1212        /// 1213        /// * Collection Owner1214        /// * Collection Admin1215        /// 1216        /// # Arguments1217        /// 1218        /// * collection_id.1219        /// 1220        /// * schema: String representing the const on-chain data schema.1221        #[weight = T::WeightInfo::set_const_on_chain_schema()]1222        pub fn set_const_on_chain_schema (1223            origin,1224            collection_id: u64,1225            schema: Vec<u8>1226        ) -> DispatchResult {1227            let sender = ensure_signed(origin)?;1228            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12291230            let mut target_collection = <Collection<T>>::get(collection_id);1231            target_collection.const_on_chain_schema = schema;1232            <Collection<T>>::insert(collection_id, target_collection);12331234            Ok(())1235        }12361237        /// Set variable on-chain data schema.1238        /// 1239        /// # Permissions1240        /// 1241        /// * Collection Owner1242        /// * Collection Admin1243        /// 1244        /// # Arguments1245        /// 1246        /// * collection_id.1247        /// 1248        /// * schema: String representing the variable on-chain data schema.1249        #[weight = T::WeightInfo::set_const_on_chain_schema()]1250        pub fn set_variable_on_chain_schema (1251            origin,1252            collection_id: u64,1253            schema: Vec<u8>1254        ) -> DispatchResult {1255            let sender = ensure_signed(origin)?;1256            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12571258            let mut target_collection = <Collection<T>>::get(collection_id);1259            target_collection.variable_on_chain_schema = schema;1260            <Collection<T>>::insert(collection_id, target_collection);12611262            Ok(())1263        }12641265        // Sudo permissions function1266        #[weight = 0]1267        pub fn set_chain_limits(1268            origin,1269            limits: ChainLimits1270        ) -> DispatchResult {1271            ensure_root(origin)?;1272            <ChainLimit>::put(limits);1273            Ok(())1274        }12751276        /// Enable smart contract self-sponsoring.1277        /// 1278        /// # Permissions1279        /// 1280        /// * Contract Owner1281        /// 1282        /// # Arguments1283        /// 1284        /// * contract address1285        /// * enable flag1286        /// 1287        #[weight = 0]1288        pub fn enable_contract_sponsoring(1289            origin,1290            contract_address: T::AccountId,1291            enable: bool1292        ) -> DispatchResult {1293            let sender = ensure_signed(origin)?;1294            let mut is_owner = false;1295            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1296                let owner = <ContractOwner<T>>::get(&contract_address);1297                is_owner = sender == owner;1298            }1299            ensure!(is_owner, "Only contract owner may call this method");13001301            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1302            Ok(())1303        }13041305    }1306}13071308impl<T: Trait> Module<T> {1309    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1310        let current_index = <ItemListIndex>::get(item.collection)1311            .checked_add(1)1312            .expect("Item list index id error");1313        let itemcopy = item.clone();1314        let owner = item.owner.clone();1315        let value = item.value as u64;13161317        Self::add_token_index(item.collection, current_index, owner.clone())?;13181319        <ItemListIndex>::insert(item.collection, current_index);1320        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);13211322        // Add current block1323        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1324        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1325        1326        // Update balance1327        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1328            .checked_add(value)1329            .unwrap();1330        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);13311332        Ok(())1333    }13341335    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1336        let current_index = <ItemListIndex>::get(item.collection)1337            .checked_add(1)1338            .expect("Item list index id error");1339        let itemcopy = item.clone();13401341        let value = item.owner.first().unwrap().fraction as u64;1342        let owner = item.owner.first().unwrap().owner.clone();13431344        Self::add_token_index(item.collection, current_index, owner.clone())?;13451346        <ItemListIndex>::insert(item.collection, current_index);1347        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);13481349        // Add current block1350        let block_number: T::BlockNumber = 0.into();1351        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);13521353        // Update balance1354        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1355            .checked_add(value)1356            .unwrap();1357        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);13581359        Ok(())1360    }13611362    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1363        let current_index = <ItemListIndex>::get(item.collection)1364            .checked_add(1)1365            .expect("Item list index id error");13661367        let item_owner = item.owner.clone();1368        let collection_id = item.collection.clone();1369        Self::add_token_index(collection_id, current_index, item.owner.clone())?;13701371        <ItemListIndex>::insert(collection_id, current_index);1372        <NftItemList<T>>::insert(collection_id, current_index, item);13731374        // Add current block1375        let block_number: T::BlockNumber = 0.into();1376        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);13771378        // Update balance1379        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1380            .checked_add(1)1381            .unwrap();1382        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);13831384        Ok(())1385    }13861387    fn burn_refungible_item(1388        collection_id: u64,1389        item_id: u64,1390        owner: T::AccountId,1391    ) -> DispatchResult {1392        ensure!(1393            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1394            "Item does not exists"1395        );1396        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1397        let item = collection1398            .owner1399            .iter()1400            .filter(|&i| i.owner == owner)1401            .next()1402            .unwrap();1403        Self::remove_token_index(collection_id, item_id, owner.clone())?;14041405        // remove approve list1406        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));14071408        // update balance1409        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1410            .checked_sub(item.fraction as u64)1411            .unwrap();1412        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);14131414        <ReFungibleItemList<T>>::remove(collection_id, item_id);14151416        Ok(())1417    }14181419    fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1420        ensure!(1421            <NftItemList<T>>::contains_key(collection_id, item_id),1422            "Item does not exists"1423        );1424        let item = <NftItemList<T>>::get(collection_id, item_id);1425        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;14261427        // remove approve list1428        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));14291430        // update balance1431        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1432            .checked_sub(1)1433            .unwrap();1434        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1435        <NftItemList<T>>::remove(collection_id, item_id);14361437        Ok(())1438    }14391440    fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1441        ensure!(1442            <FungibleItemList<T>>::contains_key(collection_id, item_id),1443            "Item does not exists"1444        );1445        let item = <FungibleItemList<T>>::get(collection_id, item_id);1446        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;14471448        // remove approve list1449        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));14501451        // update balance1452        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1453            .checked_sub(item.value as u64)1454            .unwrap();1455        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);14561457        <FungibleItemList<T>>::remove(collection_id, item_id);14581459        Ok(())1460    }14611462    fn collection_exists(collection_id: u64) -> DispatchResult {1463        ensure!(1464            <Collection<T>>::contains_key(collection_id),1465            "This collection does not exist"1466        );1467        Ok(())1468    }14691470    fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1471        Self::collection_exists(collection_id)?;14721473        let target_collection = <Collection<T>>::get(collection_id);1474        ensure!(1475            subject == target_collection.owner,1476            "You do not own this collection"1477        );14781479        Ok(())1480    }14811482    fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1483        let target_collection = <Collection<T>>::get(collection_id);1484        let mut result: bool = subject == target_collection.owner;1485        let exists = <AdminList<T>>::contains_key(collection_id);14861487        if !result & exists {1488            if <AdminList<T>>::get(collection_id).contains(&subject) {1489                result = true1490            }1491        }14921493        result1494    }14951496    fn check_owner_or_admin_permissions(1497        collection_id: u64,1498        subject: T::AccountId,1499    ) -> DispatchResult {1500        Self::collection_exists(collection_id)?;1501        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());15021503        ensure!(1504            result,1505            "You do not have permissions to modify this collection"1506        );1507        Ok(())1508    }15091510    fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1511        let target_collection = <Collection<T>>::get(collection_id);15121513        match target_collection.mode {1514            CollectionMode::NFT => {1515                <NftItemList<T>>::get(collection_id, item_id).owner == subject1516            }1517            CollectionMode::Fungible(_) => {1518                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1519            }1520            CollectionMode::ReFungible(_) => {1521                <ReFungibleItemList<T>>::get(collection_id, item_id)1522                    .owner1523                    .iter()1524                    .any(|i| i.owner == subject)1525            }1526            CollectionMode::Invalid => false,1527        }1528    }15291530    fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1531        let mes = "Address is not in white list";1532        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1533        let wl = <WhiteList<T>>::get(collection_id);1534        ensure!(wl.contains(address), mes);15351536        Ok(())1537    }15381539    fn transfer_fungible(1540        collection_id: u64,1541        item_id: u64,1542        value: u64,1543        owner: T::AccountId,1544        new_owner: T::AccountId,1545    ) -> DispatchResult {1546        ensure!(1547            <FungibleItemList<T>>::contains_key(collection_id, item_id),1548            "Item not exists"1549        );15501551        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1552        let amount = full_item.value;15531554        ensure!(amount >= value.into(), "Item balance not enouth");15551556        // update balance1557        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1558            .checked_sub(value)1559            .unwrap();1560        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);15611562        let mut new_owner_account_id = 0;1563        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1564        if new_owner_items.len() > 0 {1565            new_owner_account_id = new_owner_items[0];1566        }15671568        let val64 = value.into();15691570        // transfer1571        if amount == val64 && new_owner_account_id == 0 {1572            // change owner1573            // new owner do not have account1574            let mut new_full_item = full_item.clone();1575            new_full_item.owner = new_owner.clone();1576            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);15771578            // update balance1579            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1580                .checked_add(value)1581                .unwrap();1582            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15831584            // update index collection1585            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1586        } else {1587            let mut new_full_item = full_item.clone();1588            new_full_item.value -= val64;15891590            // separate amount1591            if new_owner_account_id > 0 {1592                // new owner has account1593                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1594                item.value += val64;15951596                // update balance1597                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1598                    .checked_add(value)1599                    .unwrap();1600                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16011602                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1603            } else {1604                // new owner do not have account1605                let item = FungibleItemType {1606                    collection: collection_id,1607                    owner: new_owner.clone(),1608                    value: val64,1609                };16101611                Self::add_fungible_item(item)?;1612            }16131614            if amount == val64 {1615                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;16161617                // remove approve list1618                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1619                <FungibleItemList<T>>::remove(collection_id, item_id);1620            }16211622            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1623        }16241625        Ok(())1626    }16271628    fn transfer_refungible(1629        collection_id: u64,1630        item_id: u64,1631        value: u64,1632        owner: T::AccountId,1633        new_owner: T::AccountId,1634    ) -> DispatchResult {1635        ensure!(1636            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1637            "Item not exists"1638        );16391640        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1641        let item = full_item1642            .owner1643            .iter()1644            .filter(|i| i.owner == owner)1645            .next()1646            .unwrap();1647        let amount = item.fraction;16481649        ensure!(amount >= value.into(), "Item balance not enouth");16501651        // update balance1652        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1653            .checked_sub(value)1654            .unwrap();1655        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);16561657        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1658            .checked_add(value)1659            .unwrap();1660        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16611662        let old_owner = item.owner.clone();1663        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1664        let val64 = value.into();16651666        // transfer1667        if amount == val64 && !new_owner_has_account {1668            // change owner1669            // new owner do not have account1670            let mut new_full_item = full_item.clone();1671            new_full_item1672                .owner1673                .iter_mut()1674                .find(|i| i.owner == owner)1675                .unwrap()1676                .owner = new_owner.clone();1677            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);16781679            // update index collection1680            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1681        } else {1682            let mut new_full_item = full_item.clone();1683            new_full_item1684                .owner1685                .iter_mut()1686                .find(|i| i.owner == owner)1687                .unwrap()1688                .fraction -= val64;16891690            // separate amount1691            if new_owner_has_account {1692                // new owner has account1693                new_full_item1694                    .owner1695                    .iter_mut()1696                    .find(|i| i.owner == new_owner)1697                    .unwrap()1698                    .fraction += val64;1699            } else {1700                // new owner do not have account1701                new_full_item.owner.push(Ownership {1702                    owner: new_owner.clone(),1703                    fraction: val64,1704                });1705                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1706            }17071708            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1709        }17101711        Ok(())1712    }17131714    fn transfer_nft(1715        collection_id: u64,1716        item_id: u64,1717        sender: T::AccountId,1718        new_owner: T::AccountId,1719    ) -> DispatchResult {1720        ensure!(1721            <NftItemList<T>>::contains_key(collection_id, item_id),1722            "Item not exists"1723        );17241725        let mut item = <NftItemList<T>>::get(collection_id, item_id);17261727        ensure!(1728            sender == item.owner,1729            "sender parameter and item owner must be equal"1730        );17311732        // update balance1733        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1734            .checked_sub(1)1735            .unwrap();1736        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);17371738        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1739            .checked_add(1)1740            .unwrap();1741        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17421743        // change owner1744        let old_owner = item.owner.clone();1745        item.owner = new_owner.clone();1746        <NftItemList<T>>::insert(collection_id, item_id, item);17471748        // update index collection1749        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;17501751        // reset approved list1752        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1753        Ok(())1754    }1755    1756    fn item_exists(1757        collection_id: u64,1758        item_id: u64,1759        mode: &CollectionMode1760    ) -> DispatchResult {1761        match mode {1762            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1763            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1764            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1765            _ => ()1766        };1767        1768        Ok(())1769    }17701771    fn set_re_fungible_variable_data(1772        collection_id: u64,1773        item_id: u64,1774        data: Vec<u8>1775    ) -> DispatchResult {1776        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);17771778        item.variable_data = data;17791780        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);17811782        Ok(())1783    }17841785    fn set_nft_variable_data(1786        collection_id: u64,1787        item_id: u64,1788        data: Vec<u8>1789    ) -> DispatchResult {1790        let mut item = <NftItemList<T>>::get(collection_id, item_id);1791        1792        item.variable_data = data;17931794        <NftItemList<T>>::insert(collection_id, item_id, item);1795        1796        Ok(())1797    }17981799    fn init_collection(item: &CollectionType<T::AccountId>) {1800        // check params1801        assert!(1802            item.decimal_points <= 4,1803            "decimal_points parameter must be lower than 4"1804        );1805        assert!(1806            item.name.len() <= 64,1807            "Collection name can not be longer than 63 char"1808        );1809        assert!(1810            item.name.len() <= 256,1811            "Collection description can not be longer than 255 char"1812        );1813        assert!(1814            item.token_prefix.len() <= 16,1815            "Token prefix can not be longer than 15 char"1816        );18171818        // Generate next collection ID1819        let next_id = CreatedCollectionCount::get()1820            .checked_add(1)1821            .expect("collection id error");18221823        CreatedCollectionCount::put(next_id);1824    }18251826    fn init_nft_token(item: &NftItemType<T::AccountId>) {1827        let current_index = <ItemListIndex>::get(item.collection)1828            .checked_add(1)1829            .expect("Item list index id error");18301831        let item_owner = item.owner.clone();1832        let collection_id = item.collection.clone();1833        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();18341835        <ItemListIndex>::insert(collection_id, current_index);18361837        // Update balance1838        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1839            .checked_add(1)1840            .unwrap();1841        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1842    }18431844    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1845        let current_index = <ItemListIndex>::get(item.collection)1846            .checked_add(1)1847            .expect("Item list index id error");1848        let owner = item.owner.clone();1849        let value = item.value as u64;18501851        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();18521853        <ItemListIndex>::insert(item.collection, current_index);18541855        // Update balance1856        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1857            .checked_add(value)1858            .unwrap();1859        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1860    }18611862    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1863        let current_index = <ItemListIndex>::get(item.collection)1864            .checked_add(1)1865            .expect("Item list index id error");18661867        let value = item.owner.first().unwrap().fraction as u64;1868        let owner = item.owner.first().unwrap().owner.clone();18691870        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();18711872        <ItemListIndex>::insert(item.collection, current_index);18731874        // Update balance1875        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1876            .checked_add(value)1877            .unwrap();1878        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1879    }18801881    fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {18821883        // add to account limit1884        if <AccountItemCount<T>>::contains_key(owner.clone()) {18851886            // bound Owned tokens by a single address1887            let count = <AccountItemCount<T>>::get(owner.clone());1888            ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");18891890            <AccountItemCount<T>>::insert(owner.clone(), 1891                count.checked_add(1).unwrap());1892        }1893        else {1894            <AccountItemCount<T>>::insert(owner.clone(), 1);1895        }18961897        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1898        if list_exists {1899            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1900            let item_contains = list.contains(&item_index.clone());19011902            if !item_contains {1903                list.push(item_index.clone());1904            }19051906            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1907        } else {1908            let mut itm = Vec::new();1909            itm.push(item_index.clone());1910            <AddressTokens<T>>::insert(collection_id, owner, itm);1911            1912        }19131914        Ok(())1915    }19161917    fn remove_token_index(1918        collection_id: u64,1919        item_index: u64,1920        owner: T::AccountId,1921    ) -> DispatchResult {19221923        // update counter1924        <AccountItemCount<T>>::insert(owner.clone(), 1925            <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());192619271928        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1929        if list_exists {1930            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1931            let item_contains = list.contains(&item_index.clone());19321933            if item_contains {1934                list.retain(|&item| item != item_index);1935                <AddressTokens<T>>::insert(collection_id, owner, list);1936            }1937        }19381939        Ok(())1940    }19411942    fn move_token_index(1943        collection_id: u64,1944        item_index: u64,1945        old_owner: T::AccountId,1946        new_owner: T::AccountId,1947    ) -> DispatchResult {1948        Self::remove_token_index(collection_id, item_index, old_owner)?;1949        Self::add_token_index(collection_id, item_index, new_owner)?;19501951        Ok(())1952    }1953}19541955////////////////////////////////////////////////////////////////////////////////////////////////////1956// Economic models1957// #region19581959/// Fee multiplier.1960pub type Multiplier = FixedU128;19611962type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1963    <T as system::Trait>::AccountId,1964>>::Balance;1965type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1966    <T as system::Trait>::AccountId,1967>>::NegativeImbalance;19681969/// Require the transactor pay for themselves and maybe include a tip to gain additional priority1970/// in the queue.1971#[derive(Encode, Decode, Clone, Eq, PartialEq)]1972pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(1973    #[codec(compact)] BalanceOf<T>1974);19751976impl<T: Trait + Send + Sync> sp_std::fmt::Debug1977    for ChargeTransactionPayment<T>1978{1979    #[cfg(feature = "std")]1980    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1981        write!(f, "ChargeTransactionPayment<{:?}>", self.0)1982    }1983    #[cfg(not(feature = "std"))]1984    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1985        Ok(())1986    }1987}19881989impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>1990where1991    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,1992    BalanceOf<T>: Send + Sync + FixedPointOperand,1993{1994    /// utility constructor. Used only in client/factory code.1995    pub fn from(fee: BalanceOf<T>) -> Self {1996        Self(fee)1997    }19981999    pub fn traditional_fee(2000        len: usize,2001        info: &DispatchInfoOf<T::Call>,2002        tip: BalanceOf<T>,2003    ) -> BalanceOf<T>2004    where2005        T::Call: Dispatchable<Info = DispatchInfo>,2006    {2007        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2008    }20092010    fn withdraw_fee(2011        &self,2012        who: &T::AccountId,2013        call: &T::Call,2014        info: &DispatchInfoOf<T::Call>,2015        len: usize,2016    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2017        let tip = self.0;20182019        // Set fee based on call type. Creating collection costs 1 Unique.2020        // All other transactions have traditional fees so far2021        // let fee = match call.is_sub_type() {2022        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2023        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2024        //                                                 // _ => <BalanceOf<T>>::from(100)2025        // };2026        let fee = Self::traditional_fee(len, info, tip);20272028        // Determine who is paying transaction fee based on ecnomic model2029        // Parse call to extract collection ID and access collection sponsor2030        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2031            Some(Call::create_item(collection_id, _properties, _owner)) => {2032                <Collection<T>>::get(collection_id).sponsor2033            }2034            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2035                let _collection_mode = <Collection<T>>::get(collection_id).mode;20362037                // sponsor timeout2038                let sponsor_transfer = match _collection_mode {2039                    CollectionMode::NFT => {2040                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2041                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2042                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2043                        if block_number >= limit_time {2044                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2045                            true2046                        }2047                        else {2048                            false2049                        }2050                    }2051                    CollectionMode::Fungible(_) => {2052                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2053                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2054                        if basket.iter().any(|i| i.address == _new_owner.clone())2055                        {2056                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2057                            let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2058                            if block_number >= limit_time {2059                                basket.retain(|x| x.address == item.address);2060                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2061                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2062                                true2063                            }2064                            else {2065                                false2066                            }2067                        }2068                        else {2069                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2070                            true2071                        }2072                    }2073                    CollectionMode::ReFungible(_) => {2074                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2075                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2076                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2077                        if block_number >= limit_time {2078                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2079                            true2080                        } else {2081                            false2082                        }2083                    }2084                    _ => {2085                        false2086                    },2087                };20882089                if !sponsor_transfer {2090                    T::AccountId::default()2091                } else {2092                    <Collection<T>>::get(collection_id).sponsor2093                }2094            }20952096            _ => T::AccountId::default(),2097        };20982099        // Sponsor smart contracts2100        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {21012102            // On instantiation: set the contract owner2103            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {21042105                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2106                    code_hash,2107                    &data,2108                    &who,2109                );2110                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());21112112                T::AccountId::default()2113            },21142115            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2116            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {21172118                let mut sp = T::AccountId::default();2119                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());2120                if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2121                    if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2122                        sp = called_contract;2123                    }2124                }21252126                sp2127            },21282129            _ => sponsor,2130        };21312132        let mut who_pays_fee: T::AccountId = sponsor.clone();2133        if sponsor == T::AccountId::default() {2134            who_pays_fee = who.clone();2135        }21362137        // Only mess with balances if fee is not zero.2138        if fee.is_zero() {2139            return Ok((fee, None));2140        }21412142        match <T as transaction_payment::Trait>::Currency::withdraw(2143            &who_pays_fee,2144            fee,2145            if tip.is_zero() {2146                WithdrawReason::TransactionPayment.into()2147            } else {2148                WithdrawReason::TransactionPayment | WithdrawReason::Tip2149            },2150            ExistenceRequirement::KeepAlive,2151        ) {2152            Ok(imbalance) => Ok((fee, Some(imbalance))),2153            Err(_) => Err(InvalidTransaction::Payment.into()),2154        }2155    }2156}215721582159impl<T: Trait + Send + Sync> SignedExtension2160    for ChargeTransactionPayment<T>2161where2162    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2163    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2164{2165    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2166    type AccountId = T::AccountId;2167    type Call = T::Call;2168    type AdditionalSigned = ();2169    type Pre = (2170        BalanceOf<T>,2171        Self::AccountId,2172        Option<NegativeImbalanceOf<T>>,2173        BalanceOf<T>,2174    );2175    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2176        Ok(())2177    }21782179    fn validate(2180        &self,2181        _who: &Self::AccountId,2182        _call: &Self::Call,2183        _info: &DispatchInfoOf<Self::Call>,2184        _len: usize,2185    ) -> TransactionValidity {2186        Ok(ValidTransaction::default())2187    }21882189    fn pre_dispatch(2190        self,2191        who: &Self::AccountId,2192        call: &Self::Call,2193        info: &DispatchInfoOf<Self::Call>,2194        len: usize,2195    ) -> Result<Self::Pre, TransactionValidityError> {2196        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2197        Ok((self.0, who.clone(), imbalance, fee))2198    }21992200    fn post_dispatch(2201        pre: Self::Pre,2202        info: &DispatchInfoOf<Self::Call>,2203        post_info: &PostDispatchInfoOf<Self::Call>,2204        len: usize,2205        _result: &DispatchResult,2206    ) -> Result<(), TransactionValidityError> {2207        let (tip, who, imbalance, fee) = pre;2208        if let Some(payed) = imbalance {2209            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2210                len as u32, info, post_info, tip,2211            );2212            let refund = fee.saturating_sub(actual_fee);2213            let actual_payment =2214                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2215                    &who, refund,2216                ) {2217                    Ok(refund_imbalance) => {2218                        // The refund cannot be larger than the up front payed max weight.2219                        // `PostDispatchInfo::calc_unspent` guards against such a case.2220                        match payed.offset(refund_imbalance) {2221                            Ok(actual_payment) => actual_payment,2222                            Err(_) => return Err(InvalidTransaction::Payment.into()),2223                        }2224                    }2225                    // We do not recreate the account using the refund. The up front payment2226                    // is gone in that case.2227                    Err(_) => payed,2228                };2229            let imbalances = actual_payment.split(tip);2230            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2231                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2232            );2233        }2234        Ok(())2235    }2236}22372238// #endregion22392240
after · pallets/nft/src/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]23#[cfg(feature = "std")]4pub use std::*;56#[cfg(feature = "std")]7pub use serde::*;89use codec::{Decode, Encode};10pub use frame_support::{11    construct_runtime, decl_event, decl_module, decl_storage, decl_error,12    dispatch::DispatchResult,13    ensure, fail, parameter_types,14    traits::{15        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,16        Randomness, WithdrawReason,17    },18    weights::{19        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},20        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,21        WeightToFeePolynomial,22    },23    IsSubType, StorageValue,24};2526use frame_system::{self as system, ensure_signed, ensure_root};27use sp_runtime::sp_std::prelude::Vec;28use sp_runtime::{29    traits::{30        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SignedExtension, Zero,31    },32    transaction_validity::{33        InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,34    },35    FixedPointOperand, FixedU128,36};37use pallet_contracts::ContractAddressFor;38use sp_runtime::traits::StaticLookup;3940#[cfg(test)]41mod mock;4243#[cfg(test)]44mod tests;4546mod default_weights;4748// Structs49// #region5051#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]52#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]53pub enum CollectionMode {54    Invalid,55    NFT,56    // decimal points57    Fungible(u32),58    // decimal points59    ReFungible(u32),60}6162impl Into<u8> for CollectionMode {63    fn into(self) -> u8 {64        match self {65            CollectionMode::Invalid => 0,66            CollectionMode::NFT => 1,67            CollectionMode::Fungible(_) => 2,68            CollectionMode::ReFungible(_) => 3,69        }70    }71}7273#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]74#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]75pub enum AccessMode {76    Normal,77    WhiteList,78}79impl Default for AccessMode {80    fn default() -> Self {81        Self::Normal82    }83}8485impl Default for CollectionMode {86    fn default() -> Self {87        Self::Invalid88    }89}9091#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]92#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]93pub struct Ownership<AccountId> {94    pub owner: AccountId,95    pub fraction: u128,96}9798#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]99#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]100pub struct CollectionType<AccountId> {101    pub owner: AccountId,102    pub mode: CollectionMode,103    pub access: AccessMode,104    pub decimal_points: u32,105    pub name: Vec<u16>,        // 64 include null escape char106    pub description: Vec<u16>, // 256 include null escape char107    pub token_prefix: Vec<u8>, // 16 include null escape char108    pub mint_mode: bool,109    pub offchain_schema: Vec<u8>,110    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender111    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship112    pub variable_on_chain_schema: Vec<u8>, //113    pub const_on_chain_schema: Vec<u8>, //114}115116#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]117#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]118pub struct NftItemType<AccountId> {119    pub collection: u64,120    pub owner: AccountId,121    pub const_data: Vec<u8>,122    pub variable_data: Vec<u8>,123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct FungibleItemType<AccountId> {128    pub collection: u64,129    pub owner: AccountId,130    pub value: u128,131}132133#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]134#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]135pub struct ReFungibleItemType<AccountId> {136    pub collection: u64,137    pub owner: Vec<Ownership<AccountId>>,138    pub const_data: Vec<u8>,139    pub variable_data: Vec<u8>,140}141142#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]143#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]144pub struct ApprovePermissions<AccountId> {145    pub approved: AccountId,146    pub amount: u64,147}148149#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]150#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]151pub struct VestingItem<AccountId, Moment> {152    pub sender: AccountId,153    pub recipient: AccountId,154    pub collection_id: u64,155    pub item_id: u64,156    pub amount: u64,157    pub vesting_date: Moment,158}159160#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]161#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]162pub struct BasketItem<AccountId, BlockNumber> {163    pub address: AccountId,164    pub start_block: BlockNumber,165}166167#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]168#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169pub struct ChainLimits {170    pub collection_numbers_limit: u64,171    pub account_token_ownership_limit: u64,172    pub collections_admins_limit: u64,173    pub custom_data_limit: u32,174175    // Timeouts for item types in passed blocks176    pub nft_sponsor_transfer_timeout: u32,177    pub fungible_sponsor_transfer_timeout: u32,178    pub refungible_sponsor_transfer_timeout: u32,179}180181pub trait WeightInfo {182	fn create_collection() -> Weight;183	fn destroy_collection() -> Weight;184	fn add_to_white_list() -> Weight;185	fn remove_from_white_list() -> Weight;186    fn set_public_access_mode() -> Weight;187    fn set_mint_permission() -> Weight;188    fn change_collection_owner() -> Weight;189    fn add_collection_admin() -> Weight;190    fn remove_collection_admin() -> Weight;191    fn set_collection_sponsor() -> Weight;192    fn confirm_sponsorship() -> Weight;193    fn remove_collection_sponsor() -> Weight;194    fn create_item(s: usize) -> Weight;195    fn burn_item() -> Weight;196    fn transfer() -> Weight;197    fn approve() -> Weight;198    fn transfer_from() -> Weight;199    fn set_offchain_schema() -> Weight;200    fn set_const_on_chain_schema() -> Weight;201    fn set_variable_on_chain_schema() -> Weight;202    fn set_variable_meta_data() -> Weight;203    // fn enable_contract_sponsoring() -> Weight;204}205206#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]207#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]208pub struct CreateNftData {209    pub const_data: Vec<u8>,210    pub variable_data: Vec<u8>,211}212213#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]214#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]215pub struct CreateFungibleData {216}217218#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]219#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]220pub struct CreateReFungibleData {221    pub const_data: Vec<u8>,222    pub variable_data: Vec<u8>,223}224225#[derive(Encode, Decode, Debug, Clone, PartialEq)]226#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]227pub enum CreateItemData {228    NFT(CreateNftData),229    Fungible(CreateFungibleData),230    ReFungible(CreateReFungibleData)231}232233impl CreateItemData {234    pub fn len(&self) -> usize {235        let len = match self {236            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),237            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),238            _ => 0239        };240        241        return len;242    }243}244245impl From<CreateNftData> for CreateItemData {246    fn from(item: CreateNftData) -> Self {247        CreateItemData::NFT(item)248    }249}250251impl From<CreateReFungibleData> for CreateItemData {252    fn from(item: CreateReFungibleData) -> Self {253        CreateItemData::ReFungible(item)254    }255}256257impl From<CreateFungibleData> for CreateItemData {258    fn from(item: CreateFungibleData) -> Self {259        CreateItemData::Fungible(item)260    }261}262263264decl_error! {265	/// Error for non-fungible-token module.266	pub enum Error for Module<T: Trait> {267        /// Total collections bound exceeded268        TotalCollectionsLimitExceeded,269		/// Decimal_points parameter must be lower than 4270        CollectionDecimalPointLimitExceeded, 271        /// Collection name can not be longer than 63 char272        CollectionNameLimitExceeded, 273        /// Collection description can not be longer than 255 char274        CollectionDescriptionLimitExceeded, 275        /// Token prefix can not be longer than 15 char276        CollectionTokenPrefixLimitExceeded,277        /// This collection does not exist278        CollectionNotFound,279        /// Item not exists280        TokenNotFound,281        /// Arithmetic calculation overflow282        NumOverflow,       283        /// Account already has admin role284        AlreadyAdmin,  285        /// You do not own this collection286        NoPermission,287        /// This address is not set as sponsor, use setCollectionSponsor first288        ConfirmUnsetSponsorFail,289        /// Collection is not in mint mode290        PublicMintingNotAllowed,291        /// Sender parameter and item owner must be equal292        MustBeTokenOwner,293        /// Item balance not enouth294        TokenValueTooLow,295        /// Size of item is too large296        NftSizeLimitExceeded,297        /// Size of item must be 0 with fungible type298        FungibleUnexpectedParam,299        /// No approve found300        ApproveNotFound,301        /// Requested value more than approved302        TokenValueNotEnough,303        /// Only approved addresses can call this method304        ApproveRequired,305        /// Address is not in white list306        AddresNotInWhiteList,307        /// Number of collection admins bound exceeded308        CollectionAdminsLimitExceeded,309        /// Owned tokens by a single address bound exceeded310        AddressOwnershipLimitExceeded,311        /// Length of items properties must be greater than 0312        EmptyArgument,313	}314}315316pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {317    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;318319    /// Weight information for extrinsics in this pallet.320	type WeightInfo: WeightInfo;321}322323#[cfg(feature = "runtime-benchmarks")]324mod benchmarking;325326// #endregion327328decl_storage! {329    trait Store for Module<T: Trait> as Nft {330331        // Private members332        NextCollectionID: u64;333        CreatedCollectionCount: u64;334        ChainVersion: u64;335        ItemListIndex: map hasher(blake2_128_concat) u64 => u64;336337        // Chain limits struct338        pub ChainLimit get(fn chain_limit) config(): ChainLimits;339340        // Bound counters341        CollectionCount: u64;342        pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;343344        // Basic collections345        pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;346        pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;347        pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;348349        /// Balance owner per collection map350        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;351352        /// second parameter: item id + owner account id353        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;354355        /// Item collections356        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;357        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;358        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;359360        /// Index list361        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;362363        /// Tokens transfer baskets364        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;365        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => Vec<BasketItem<T::AccountId, T::BlockNumber>>;366        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;367368        // Contract Sponsorship and Ownership369        pub ContractOwner get(fn contract_owner): map hasher(identity) T::AccountId => T::AccountId;370        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(identity) T::AccountId => bool;371    }372    add_extra_genesis {373        build(|config: &GenesisConfig<T>| {374            // Modification of storage375            for (_num, _c) in &config.collection {376                <Module<T>>::init_collection(_c);377            }378379            for (_num, _q, _i) in &config.nft_item_id {380                <Module<T>>::init_nft_token(_i);381            }382383            for (_num, _q, _i) in &config.fungible_item_id {384                <Module<T>>::init_fungible_token(_i);385            }386387            for (_num, _q, _i) in &config.refungible_item_id {388                <Module<T>>::init_refungible_token(_i);389            }390        })391    }392}393394decl_event!(395    pub enum Event<T>396    where397        AccountId = <T as system::Trait>::AccountId,398    {399        /// New collection was created400        /// 401        /// # Arguments402        /// 403        /// * collection_id: Globally unique identifier of newly created collection.404        /// 405        /// * mode: [CollectionMode] converted into u8.406        /// 407        /// * account_id: Collection owner.408        Created(u64, u8, AccountId),409410        /// New item was created.411        /// 412        /// # Arguments413        /// 414        /// * collection_id: Id of the collection where item was created.415        /// 416        /// * item_id: Id of an item. Unique within the collection.417        ItemCreated(u64, u64),418419        /// Collection item was burned.420        /// 421        /// # Arguments422        /// 423        /// collection_id.424        /// 425        /// item_id: Identifier of burned NFT.426        ItemDestroyed(u64, u64),427    }428);429430decl_module! {431    pub struct Module<T: Trait> for enum Call where origin: T::Origin {432433        fn deposit_event() = default;434        type Error = Error<T>;435436        fn on_initialize(now: T::BlockNumber) -> Weight {437438            if ChainVersion::get() < 2439            {440                let value = NextCollectionID::get();441                CreatedCollectionCount::put(value);442                ChainVersion::put(2);443            }444445            0446        }447448        /// 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.449        /// 450        /// # Permissions451        /// 452        /// * Anyone.453        /// 454        /// # Arguments455        /// 456        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.457        /// 458        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.459        /// 460        /// * token_prefix: UTF-8 string with token prefix.461        /// 462        /// * mode: [CollectionMode] collection type and type dependent data.463        // returns collection ID464        #[weight = T::WeightInfo::create_collection()]465        pub fn create_collection(origin,466                                 collection_name: Vec<u16>,467                                 collection_description: Vec<u16>,468                                 token_prefix: Vec<u8>,469                                 mode: CollectionMode) -> DispatchResult {470471            // Anyone can create a collection472            let who = ensure_signed(origin)?;473474            let decimal_points = match mode {475                CollectionMode::Fungible(points) => points,476                CollectionMode::ReFungible(points) => points,477                _ => 0478            };479480            // bound Total number of collections481            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);482483            // check params484            ensure!(decimal_points <= 4, Error::<T>::CollectionDecimalPointLimitExceeded);485486            let mut name = collection_name.to_vec();487            name.push(0);488            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);489490            let mut description = collection_description.to_vec();491            description.push(0);492            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);493494            let mut prefix = token_prefix.to_vec();495            prefix.push(0);496            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);497498            // Generate next collection ID499            let next_id = CreatedCollectionCount::get()500                .checked_add(1)501                .ok_or(Error::<T>::NumOverflow)?;502503            // bound counter504            let total = CollectionCount::get()505                .checked_add(1)506                .ok_or(Error::<T>::NumOverflow)?;507508            CreatedCollectionCount::put(next_id);509            CollectionCount::put(total);510511            // Create new collection512            let new_collection = CollectionType {513                owner: who.clone(),514                name: name,515                mode: mode.clone(),516                mint_mode: false,517                access: AccessMode::Normal,518                description: description,519                decimal_points: decimal_points,520                token_prefix: prefix,521                offchain_schema: Vec::new(),522                sponsor: T::AccountId::default(),523                unconfirmed_sponsor: T::AccountId::default(),524                variable_on_chain_schema: Vec::new(),525                const_on_chain_schema: Vec::new(),526            };527528            // Add new collection to map529            <Collection<T>>::insert(next_id, new_collection);530531            // call event532            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));533534            Ok(())535        }536537        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.538        /// 539        /// # Permissions540        /// 541        /// * Collection Owner.542        /// 543        /// # Arguments544        /// 545        /// * collection_id: collection to destroy.546        #[weight = T::WeightInfo::destroy_collection()]547        pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {548549            let sender = ensure_signed(origin)?;550            Self::check_owner_permissions(collection_id, sender)?;551552            <AddressTokens<T>>::remove_prefix(collection_id);553            <ApprovedList<T>>::remove_prefix(collection_id);554            <Balance<T>>::remove_prefix(collection_id);555            <ItemListIndex>::remove(collection_id);556            <AdminList<T>>::remove(collection_id);557            <Collection<T>>::remove(collection_id);558            <WhiteList<T>>::remove(collection_id);559560            <NftItemList<T>>::remove_prefix(collection_id);561            <FungibleItemList<T>>::remove_prefix(collection_id);562            <ReFungibleItemList<T>>::remove_prefix(collection_id);563564            <NftTransferBasket<T>>::remove_prefix(collection_id);565            <FungibleTransferBasket<T>>::remove_prefix(collection_id);566            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);567568            if CollectionCount::get() > 0569            {570                // bound couter571                let total = CollectionCount::get()572                    .checked_sub(1)573                    .ok_or(Error::<T>::NumOverflow)?;574575                CollectionCount::put(total);576            }577578            Ok(())579        }580581        /// Add an address to white list.582        /// 583        /// # Permissions584        /// 585        /// * Collection Owner586        /// * Collection Admin587        /// 588        /// # Arguments589        /// 590        /// * collection_id.591        /// 592        /// * address.593        #[weight = T::WeightInfo::add_to_white_list()]594        pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{595596            let sender = ensure_signed(origin)?;597            Self::check_owner_or_admin_permissions(collection_id, sender)?;598599            let mut white_list_collection: Vec<T::AccountId>;600            if <WhiteList<T>>::contains_key(collection_id) {601                white_list_collection = <WhiteList<T>>::get(collection_id);602                if !white_list_collection.contains(&address.clone())603                {604                    white_list_collection.push(address.clone());605                }606            }607            else {608                white_list_collection = Vec::new();609                white_list_collection.push(address.clone());610            }611612            <WhiteList<T>>::insert(collection_id, white_list_collection);613            Ok(())614        }615616        /// Remove an address from white list.617        /// 618        /// # Permissions619        /// 620        /// * Collection Owner621        /// * Collection Admin622        /// 623        /// # Arguments624        /// 625        /// * collection_id.626        /// 627        /// * address.628        #[weight = T::WeightInfo::remove_from_white_list()]629        pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{630631            let sender = ensure_signed(origin)?;632            Self::check_owner_or_admin_permissions(collection_id, sender)?;633634            if <WhiteList<T>>::contains_key(collection_id) {635                let mut white_list_collection = <WhiteList<T>>::get(collection_id);636                if white_list_collection.contains(&address.clone())637                {638                    white_list_collection.retain(|i| *i != address.clone());639                    <WhiteList<T>>::insert(collection_id, white_list_collection);640                }641            }642643            Ok(())644        }645646        /// Toggle between normal and white list access for the methods with access for `Anyone`.647        /// 648        /// # Permissions649        /// 650        /// * Collection Owner.651        /// 652        /// # Arguments653        /// 654        /// * collection_id.655        /// 656        /// * mode: [AccessMode]657        #[weight = T::WeightInfo::set_public_access_mode()]658        pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult659        {660            let sender = ensure_signed(origin)?;661662            Self::check_owner_permissions(collection_id, sender)?;663            let mut target_collection = <Collection<T>>::get(collection_id);664            target_collection.access = mode;665            <Collection<T>>::insert(collection_id, target_collection);666667            Ok(())668        }669670        /// Allows Anyone to create tokens if:671        /// * White List is enabled, and672        /// * Address is added to white list, and673        /// * This method was called with True parameter674        /// 675        /// # Permissions676        /// * Collection Owner677        ///678        /// # Arguments679        /// 680        /// * collection_id.681        /// 682        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.683        #[weight = T::WeightInfo::set_mint_permission()]684        pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult685        {686            let sender = ensure_signed(origin)?;687688            Self::check_owner_permissions(collection_id, sender)?;689            let mut target_collection = <Collection<T>>::get(collection_id);690            target_collection.mint_mode = mint_permission;691            <Collection<T>>::insert(collection_id, target_collection);692693            Ok(())694        }695696        /// Change the owner of the collection.697        /// 698        /// # Permissions699        /// 700        /// * Collection Owner.701        /// 702        /// # Arguments703        /// 704        /// * collection_id.705        /// 706        /// * new_owner.707        #[weight = T::WeightInfo::change_collection_owner()]708        pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {709710            let sender = ensure_signed(origin)?;711            Self::check_owner_permissions(collection_id, sender)?;712            let mut target_collection = <Collection<T>>::get(collection_id);713            target_collection.owner = new_owner;714            <Collection<T>>::insert(collection_id, target_collection);715716            Ok(())717        }718719        /// Adds an admin of the Collection.720        /// 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. 721        /// 722        /// # Permissions723        /// 724        /// * Collection Owner.725        /// * Collection Admin.726        /// 727        /// # Arguments728        /// 729        /// * collection_id: ID of the Collection to add admin for.730        /// 731        /// * new_admin_id: Address of new admin to add.732        #[weight = T::WeightInfo::add_collection_admin()]733        pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {734735            let sender = ensure_signed(origin)?;736            Self::check_owner_or_admin_permissions(collection_id, sender)?;737            let mut admin_arr: Vec<T::AccountId> = Vec::new();738739            if <AdminList<T>>::contains_key(collection_id)740            {741                admin_arr = <AdminList<T>>::get(collection_id);742                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);743            }744745            // Number of collection admins746            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);747748            admin_arr.push(new_admin_id);749            <AdminList<T>>::insert(collection_id, admin_arr);750751            Ok(())752        }753754        /// 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.755        ///756        /// # Permissions757        /// 758        /// * Collection Owner.759        /// * Collection Admin.760        /// 761        /// # Arguments762        /// 763        /// * collection_id: ID of the Collection to remove admin for.764        /// 765        /// * account_id: Address of admin to remove.766        #[weight = T::WeightInfo::remove_collection_admin()]767        pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {768769            let sender = ensure_signed(origin)?;770            Self::check_owner_or_admin_permissions(collection_id, sender)?;771772            if <AdminList<T>>::contains_key(collection_id)773            {774                let mut admin_arr = <AdminList<T>>::get(collection_id);775                admin_arr.retain(|i| *i != account_id);776                <AdminList<T>>::insert(collection_id, admin_arr);777            }778779            Ok(())780        }781782        /// # Permissions783        /// 784        /// * Collection Owner785        /// 786        /// # Arguments787        /// 788        /// * collection_id.789        /// 790        /// * new_sponsor.791        #[weight = T::WeightInfo::set_collection_sponsor()]792        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {793794            let sender = ensure_signed(origin)?;795            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);796797            let mut target_collection = <Collection<T>>::get(collection_id);798            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);799800            target_collection.unconfirmed_sponsor = new_sponsor;801            <Collection<T>>::insert(collection_id, target_collection);802803            Ok(())804        }805806        /// # Permissions807        /// 808        /// * Sponsor.809        /// 810        /// # Arguments811        /// 812        /// * collection_id.813        #[weight = T::WeightInfo::confirm_sponsorship()]814        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {815816            let sender = ensure_signed(origin)?;817            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);818819            let mut target_collection = <Collection<T>>::get(collection_id);820            ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);821822            target_collection.sponsor = target_collection.unconfirmed_sponsor;823            target_collection.unconfirmed_sponsor = T::AccountId::default();824            <Collection<T>>::insert(collection_id, target_collection);825826            Ok(())827        }828829        /// Switch back to pay-per-own-transaction model.830        ///831        /// # Permissions832        ///833        /// * Collection owner.834        /// 835        /// # Arguments836        /// 837        /// * collection_id.838        #[weight = T::WeightInfo::remove_collection_sponsor()]839        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {840841            let sender = ensure_signed(origin)?;842            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);843844            let mut target_collection = <Collection<T>>::get(collection_id);845            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);846847            target_collection.sponsor = T::AccountId::default();848            <Collection<T>>::insert(collection_id, target_collection);849850            Ok(())851        }852853        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.854        /// 855        /// # Permissions856        /// 857        /// * Collection Owner.858        /// * Collection Admin.859        /// * Anyone if860        ///     * White List is enabled, and861        ///     * Address is added to white list, and862        ///     * MintPermission is enabled (see SetMintPermission method)863        /// 864        /// # Arguments865        /// 866        /// * collection_id: ID of the collection.867        /// 868        /// * owner: Address, initial owner of the NFT.869        ///870        /// * data: Token data to store on chain.871        // #[weight =872        // (130_000_000 as Weight)873        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))874        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))875        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]876877        #[weight = T::WeightInfo::create_item(data.len())]878        pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {879880            let sender = ensure_signed(origin)?;881882            Self::collection_exists(collection_id)?;883884            let target_collection = <Collection<T>>::get(collection_id);885886            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;887            Self::validate_create_item_args(&target_collection, &data)?;888            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;889890            Ok(())891        }892893        /// This method creates multiple instances of NFT Collection created with CreateCollection method.894        /// 895        /// # Permissions896        /// 897        /// * Collection Owner.898        /// * Collection Admin.899        /// * Anyone if900        ///     * White List is enabled, and901        ///     * Address is added to white list, and902        ///     * MintPermission is enabled (see SetMintPermission method)903        /// 904        /// # Arguments905        /// 906        /// * collection_id: ID of the collection.907        /// 908        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].909        /// 910        /// * owner: Address, initial owner of the NFT.911        #[weight = T::WeightInfo::create_item(items_data.into_iter()912                               .map(|data| { data.len() })913                               .sum())]914        pub fn create_multiple_items(origin, collection_id: u64, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {915916            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);917            let sender = ensure_signed(origin)?;918919            Self::collection_exists(collection_id)?;920            let target_collection = <Collection<T>>::get(collection_id);921922            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;923924            for data in &items_data {925                Self::validate_create_item_args(&target_collection, data)?;926            }927            for data in &items_data {928                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;929            }930931            Ok(())932        }933934        /// Destroys a concrete instance of NFT.935        /// 936        /// # Permissions937        /// 938        /// * Collection Owner.939        /// * Collection Admin.940        /// * Current NFT Owner.941        /// 942        /// # Arguments943        /// 944        /// * collection_id: ID of the collection.945        /// 946        /// * item_id: ID of NFT to burn.947        #[weight = T::WeightInfo::burn_item()]948        pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {949950            let sender = ensure_signed(origin)?;951            Self::collection_exists(collection_id)?;952953            // Transfer permissions check954            let target_collection = <Collection<T>>::get(collection_id);955            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||956                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),957                Error::<T>::NoPermission);958959            if target_collection.access == AccessMode::WhiteList {960                Self::check_white_list(collection_id, &sender)?;961            }962963            match target_collection.mode964            {965                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,966                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,967                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,968                _ => ()969            };970971            // call event972            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));973974            Ok(())975        }976977        /// Change ownership of the token.978        /// 979        /// # Permissions980        /// 981        /// * Collection Owner982        /// * Collection Admin983        /// * Current NFT owner984        ///985        /// # Arguments986        /// 987        /// * recipient: Address of token recipient.988        /// 989        /// * collection_id.990        /// 991        /// * item_id: ID of the item992        ///     * Non-Fungible Mode: Required.993        ///     * Fungible Mode: Ignored.994        ///     * Re-Fungible Mode: Required.995        /// 996        /// * value: Amount to transfer.997        ///     * Non-Fungible Mode: Ignored998        ///     * Fungible Mode: Must specify transferred amount999        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1000        #[weight = T::WeightInfo::transfer()]1001        pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {10021003            let sender = ensure_signed(origin)?;10041005            // Transfer permissions check1006            let target_collection = <Collection<T>>::get(collection_id);1007            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1008                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1009                Error::<T>::NoPermission);10101011            if target_collection.access == AccessMode::WhiteList {1012                Self::check_white_list(collection_id, &sender)?;1013                Self::check_white_list(collection_id, &recipient)?;1014            }10151016            match target_collection.mode1017            {1018                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1019                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1020                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1021                _ => ()1022            };10231024            Ok(())1025        }10261027        /// Set, change, or remove approved address to transfer the ownership of the NFT.1028        /// 1029        /// # Permissions1030        /// 1031        /// * Collection Owner1032        /// * Collection Admin1033        /// * Current NFT owner1034        /// 1035        /// # Arguments1036        /// 1037        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1038        /// 1039        /// * collection_id.1040        /// 1041        /// * item_id: ID of the item.1042        #[weight = T::WeightInfo::approve()]1043        pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {10441045            let sender = ensure_signed(origin)?;10461047            // Transfer permissions check1048            let target_collection = <Collection<T>>::get(collection_id);1049            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1050                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1051                Error::<T>::NoPermission);10521053            if target_collection.access == AccessMode::WhiteList {1054                Self::check_white_list(collection_id, &sender)?;1055                Self::check_white_list(collection_id, &approved)?;1056            }10571058            // amount param stub1059            let amount = 100000000;10601061            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1062            if list_exists {10631064                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1065                let item_contains = list.iter().any(|i| i.approved == approved);10661067                if !item_contains {1068                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1069                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1070                }1071            } else {10721073                let mut list = Vec::new();1074                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1075                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1076            }10771078            Ok(())1079        }1080        1081        /// 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.1082        /// 1083        /// # Permissions1084        /// * Collection Owner1085        /// * Collection Admin1086        /// * Current NFT owner1087        /// * Address approved by current NFT owner1088        /// 1089        /// # Arguments1090        /// 1091        /// * from: Address that owns token.1092        /// 1093        /// * recipient: Address of token recipient.1094        /// 1095        /// * collection_id.1096        /// 1097        /// * item_id: ID of the item.1098        /// 1099        /// * value: Amount to transfer.1100        #[weight = T::WeightInfo::transfer_from()]1101        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {11021103            let sender = ensure_signed(origin)?;1104            let mut appoved_transfer = false;11051106            // Check approve1107            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1108                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1109                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1110                if opt_item.is_some()1111                {1112                    appoved_transfer = true;1113                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1114                }1115            }11161117            // Transfer permissions check1118            let target_collection = <Collection<T>>::get(collection_id);1119                ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1120                Error::<T>::NoPermission);11211122            if target_collection.access == AccessMode::WhiteList {1123                Self::check_white_list(collection_id, &sender)?;1124                Self::check_white_list(collection_id, &recipient)?;1125            }11261127            // remove approve1128            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1129                .into_iter().filter(|i| i.approved != sender.clone()).collect();1130            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);113111321133            match target_collection.mode1134            {1135                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1136                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1137                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1138                _ => ()1139            };11401141            Ok(())1142        }11431144        ///1145        #[weight = 0]1146        pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11471148            // let no_perm_mes = "You do not have permissions to modify this collection";1149            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1150            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1151            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11521153            // // on_nft_received  call11541155            // Self::transfer(origin, collection_id, item_id, new_owner)?;11561157            Ok(())1158        }11591160        /// Set off-chain data schema.1161        /// 1162        /// # Permissions1163        /// 1164        /// * Collection Owner1165        /// * Collection Admin1166        /// 1167        /// # Arguments1168        /// 1169        /// * collection_id.1170        /// 1171        /// * schema: String representing the offchain data schema.1172        #[weight = T::WeightInfo::set_variable_meta_data()]1173        pub fn set_variable_meta_data (1174            origin,1175            collection_id: u64,1176            item_id: u64,1177            data: Vec<u8>1178        ) -> DispatchResult {1179            let sender = ensure_signed(origin)?;1180            1181            Self::collection_exists(collection_id)?;11821183            // Modify permissions check1184            let target_collection = <Collection<T>>::get(collection_id);1185            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1186                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1187                Error::<T>::NoPermission);11881189            Self::item_exists(collection_id, item_id, &target_collection.mode)?;11901191            match target_collection.mode1192            {1193                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1194                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1195                CollectionMode::Fungible(_) => fail!("Can't store metadata in fungible tokens."),1196                _ => fail!("Unexpected collection type.")1197            };11981199            Ok(())1200        }1201        12021203        /// Set off-chain data schema.1204        /// 1205        /// # Permissions1206        /// 1207        /// * Collection Owner1208        /// * Collection Admin1209        /// 1210        /// # Arguments1211        /// 1212        /// * collection_id.1213        /// 1214        /// * schema: String representing the offchain data schema.1215        #[weight = T::WeightInfo::set_offchain_schema()]1216        pub fn set_offchain_schema(1217            origin,1218            collection_id: u64,1219            schema: Vec<u8>1220        ) -> DispatchResult {1221            let sender = ensure_signed(origin)?;1222            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12231224            let mut target_collection = <Collection<T>>::get(collection_id);1225            target_collection.offchain_schema = schema;1226            <Collection<T>>::insert(collection_id, target_collection);12271228            Ok(())1229        }12301231        /// Set const on-chain data schema.1232        /// 1233        /// # Permissions1234        /// 1235        /// * Collection Owner1236        /// * Collection Admin1237        /// 1238        /// # Arguments1239        /// 1240        /// * collection_id.1241        /// 1242        /// * schema: String representing the const on-chain data schema.1243        #[weight = T::WeightInfo::set_const_on_chain_schema()]1244        pub fn set_const_on_chain_schema (1245            origin,1246            collection_id: u64,1247            schema: Vec<u8>1248        ) -> DispatchResult {1249            let sender = ensure_signed(origin)?;1250            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12511252            let mut target_collection = <Collection<T>>::get(collection_id);1253            target_collection.const_on_chain_schema = schema;1254            <Collection<T>>::insert(collection_id, target_collection);12551256            Ok(())1257        }12581259        /// Set variable on-chain data schema.1260        /// 1261        /// # Permissions1262        /// 1263        /// * Collection Owner1264        /// * Collection Admin1265        /// 1266        /// # Arguments1267        /// 1268        /// * collection_id.1269        /// 1270        /// * schema: String representing the variable on-chain data schema.1271        #[weight = T::WeightInfo::set_const_on_chain_schema()]1272        pub fn set_variable_on_chain_schema (1273            origin,1274            collection_id: u64,1275            schema: Vec<u8>1276        ) -> DispatchResult {1277            let sender = ensure_signed(origin)?;1278            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12791280            let mut target_collection = <Collection<T>>::get(collection_id);1281            target_collection.variable_on_chain_schema = schema;1282            <Collection<T>>::insert(collection_id, target_collection);12831284            Ok(())1285        }12861287        // Sudo permissions function1288        #[weight = 0]1289        pub fn set_chain_limits(1290            origin,1291            limits: ChainLimits1292        ) -> DispatchResult {1293            ensure_root(origin)?;1294            <ChainLimit>::put(limits);1295            Ok(())1296        }12971298        /// Enable smart contract self-sponsoring.1299        /// 1300        /// # Permissions1301        /// 1302        /// * Contract Owner1303        /// 1304        /// # Arguments1305        /// 1306        /// * contract address1307        /// * enable flag1308        /// 1309        #[weight = 0]1310        pub fn enable_contract_sponsoring(1311            origin,1312            contract_address: T::AccountId,1313            enable: bool1314        ) -> DispatchResult {1315            let sender = ensure_signed(origin)?;1316            let mut is_owner = false;1317            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1318                let owner = <ContractOwner<T>>::get(&contract_address);1319                is_owner = sender == owner;1320            }1321            ensure!(is_owner, Error::<T>::NoPermission);13221323            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1324            Ok(())1325        }13261327    }1328}13291330impl<T: Trait> Module<T> {13311332    fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {13331334        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1335            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1336            Self::check_white_list(collection_id, owner)?;1337            Self::check_white_list(collection_id, sender)?;1338        }13391340        Ok(())1341    }13421343    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1344        match target_collection.mode1345        {1346            CollectionMode::NFT => {1347                if let CreateItemData::NFT(data) = data {1348                    // check sizes1349                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");1350                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");1351                } else {1352                    fail!("Not NFT item data used to mint in NFT collection.");1353                }1354            },1355            CollectionMode::Fungible(_) => {1356                if let CreateItemData::Fungible(_) = data {1357                } else {1358                    fail!("Not Fungible item data used to mint in Fungible collection.");1359                }1360            },1361            CollectionMode::ReFungible(_) => {1362                if let CreateItemData::ReFungible(data) = data {13631364                    // check sizes1365                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");1366                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");1367                } else {1368                    fail!("Not Re Fungible item data used to mint in Re Fungible collection.");1369                }1370            },1371            _ => { fail!("Unexpected collection type."); }1372        };13731374        Ok(())1375    }13761377    fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1378        match data1379        {1380            CreateItemData::NFT(data) => {1381                let item = NftItemType {1382                    collection: collection_id,1383                    owner,1384                    const_data: data.const_data,1385                    variable_data: data.variable_data1386                };13871388                Self::add_nft_item(item)?;1389            },1390            CreateItemData::Fungible(_) => {1391                let item = FungibleItemType {1392                    collection: collection_id,1393                    owner,1394                    value: (10 as u128).pow(collection.decimal_points)1395                };13961397                Self::add_fungible_item(item)?;1398            },1399            CreateItemData::ReFungible(data) => {1400                let mut owner_list = Vec::new();1401                let value = (10 as u128).pow(collection.decimal_points);1402                owner_list.push(Ownership {owner: owner.clone(), fraction: value});14031404                let item = ReFungibleItemType {1405                    collection: collection_id,1406                    owner: owner_list,1407                    const_data: data.const_data,1408                    variable_data: data.variable_data1409                };14101411                Self::add_refungible_item(item)?;1412            }1413        };141414151416        // call event1417        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));14181419        Ok(())1420    }14211422    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1423        let current_index = <ItemListIndex>::get(item.collection)1424            .checked_add(1)1425            .ok_or(Error::<T>::NumOverflow)?;1426        let itemcopy = item.clone();1427        let owner = item.owner.clone();1428        let value = item.value as u64;14291430        Self::add_token_index(item.collection, current_index, owner.clone())?;14311432        <ItemListIndex>::insert(item.collection, current_index);1433        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14341435        // Add current block1436        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1437        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1438        1439        // Update balance1440        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1441            .checked_add(value)1442            .ok_or(Error::<T>::NumOverflow)?;1443        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14441445        Ok(())1446    }14471448    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1449        let current_index = <ItemListIndex>::get(item.collection)1450            .checked_add(1)1451            .ok_or(Error::<T>::NumOverflow)?;1452        let itemcopy = item.clone();14531454        let value = item.owner.first().unwrap().fraction as u64;1455        let owner = item.owner.first().unwrap().owner.clone();14561457        Self::add_token_index(item.collection, current_index, owner.clone())?;14581459        <ItemListIndex>::insert(item.collection, current_index);1460        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14611462        // Add current block1463        let block_number: T::BlockNumber = 0.into();1464        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);14651466        // Update balance1467        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1468            .checked_add(value)1469            .ok_or(Error::<T>::NumOverflow)?;1470        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14711472        Ok(())1473    }14741475    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1476        let current_index = <ItemListIndex>::get(item.collection)1477            .checked_add(1)1478            .ok_or(Error::<T>::NumOverflow)?;14791480        let item_owner = item.owner.clone();1481        let collection_id = item.collection.clone();1482        Self::add_token_index(collection_id, current_index, item.owner.clone())?;14831484        <ItemListIndex>::insert(collection_id, current_index);1485        <NftItemList<T>>::insert(collection_id, current_index, item);14861487        // Add current block1488        let block_number: T::BlockNumber = 0.into();1489        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);14901491        // Update balance1492        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1493            .checked_add(1)1494            .ok_or(Error::<T>::NumOverflow)?;1495        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);14961497        Ok(())1498    }14991500    fn burn_refungible_item(1501        collection_id: u64,1502        item_id: u64,1503        owner: T::AccountId,1504    ) -> DispatchResult {1505        ensure!(1506            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1507            Error::<T>::TokenNotFound1508        );1509        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1510        let item = collection1511            .owner1512            .iter()1513            .filter(|&i| i.owner == owner)1514            .next()1515            .unwrap();1516        Self::remove_token_index(collection_id, item_id, owner.clone())?;15171518        // remove approve list1519        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));15201521        // update balance1522        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1523            .checked_sub(item.fraction as u64)1524            .ok_or(Error::<T>::NumOverflow)?;1525        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15261527        <ReFungibleItemList<T>>::remove(collection_id, item_id);15281529        Ok(())1530    }15311532    fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1533        ensure!(1534            <NftItemList<T>>::contains_key(collection_id, item_id),1535            Error::<T>::TokenNotFound1536        );1537        let item = <NftItemList<T>>::get(collection_id, item_id);1538        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15391540        // remove approve list1541        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15421543        // update balance1544        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1545            .checked_sub(1)1546            .ok_or(Error::<T>::NumOverflow)?;1547        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1548        <NftItemList<T>>::remove(collection_id, item_id);15491550        Ok(())1551    }15521553    fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1554        ensure!(1555            <FungibleItemList<T>>::contains_key(collection_id, item_id),1556            Error::<T>::TokenNotFound1557        );1558        let item = <FungibleItemList<T>>::get(collection_id, item_id);1559        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15601561        // remove approve list1562        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15631564        // update balance1565        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1566            .checked_sub(item.value as u64)1567            .ok_or(Error::<T>::NumOverflow)?;1568        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15691570        <FungibleItemList<T>>::remove(collection_id, item_id);15711572        Ok(())1573    }15741575    fn collection_exists(collection_id: u64) -> DispatchResult {1576        ensure!(1577            <Collection<T>>::contains_key(collection_id),1578            Error::<T>::CollectionNotFound1579        );1580        Ok(())1581    }15821583    fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1584        Self::collection_exists(collection_id)?;15851586        let target_collection = <Collection<T>>::get(collection_id);1587        ensure!(1588            subject == target_collection.owner,1589            Error::<T>::NoPermission1590        );15911592        Ok(())1593    }15941595    fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1596        let target_collection = <Collection<T>>::get(collection_id);1597        let mut result: bool = subject == target_collection.owner;1598        let exists = <AdminList<T>>::contains_key(collection_id);15991600        if !result & exists {1601            if <AdminList<T>>::get(collection_id).contains(&subject) {1602                result = true1603            }1604        }16051606        result1607    }16081609    fn check_owner_or_admin_permissions(1610        collection_id: u64,1611        subject: T::AccountId,1612    ) -> DispatchResult {1613        Self::collection_exists(collection_id)?;1614        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());16151616        ensure!(1617            result,1618            Error::<T>::NoPermission1619        );1620        Ok(())1621    }16221623    fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1624        let target_collection = <Collection<T>>::get(collection_id);16251626        match target_collection.mode {1627            CollectionMode::NFT => {1628                <NftItemList<T>>::get(collection_id, item_id).owner == subject1629            }1630            CollectionMode::Fungible(_) => {1631                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1632            }1633            CollectionMode::ReFungible(_) => {1634                <ReFungibleItemList<T>>::get(collection_id, item_id)1635                    .owner1636                    .iter()1637                    .any(|i| i.owner == subject)1638            }1639            CollectionMode::Invalid => false,1640        }1641    }16421643    fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1644        let mes = Error::<T>::AddresNotInWhiteList;1645        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1646        let wl = <WhiteList<T>>::get(collection_id);1647        ensure!(wl.contains(address), mes);16481649        Ok(())1650    }16511652    fn transfer_fungible(1653        collection_id: u64,1654        item_id: u64,1655        value: u64,1656        owner: T::AccountId,1657        new_owner: T::AccountId,1658    ) -> DispatchResult {1659        ensure!(1660            <FungibleItemList<T>>::contains_key(collection_id, item_id),1661            Error::<T>::TokenNotFound1662        );16631664        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1665        let amount = full_item.value;16661667        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);16681669        // update balance1670        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1671            .checked_sub(value)1672            .ok_or(Error::<T>::NumOverflow)?;1673        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);16741675        let mut new_owner_account_id = 0;1676        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1677        if new_owner_items.len() > 0 {1678            new_owner_account_id = new_owner_items[0];1679        }16801681        let val64 = value.into();16821683        // transfer1684        if amount == val64 && new_owner_account_id == 0 {1685            // change owner1686            // new owner do not have account1687            let mut new_full_item = full_item.clone();1688            new_full_item.owner = new_owner.clone();1689            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);16901691            // update balance1692            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1693                .checked_add(value)1694                .ok_or(Error::<T>::NumOverflow)?;1695            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16961697            // update index collection1698            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1699        } else {1700            let mut new_full_item = full_item.clone();1701            new_full_item.value -= val64;17021703            // separate amount1704            if new_owner_account_id > 0 {1705                // new owner has account1706                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1707                item.value += val64;17081709                // update balance1710                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1711                    .checked_add(value)1712                    .ok_or(Error::<T>::NumOverflow)?;1713                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17141715                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1716            } else {1717                // new owner do not have account1718                let item = FungibleItemType {1719                    collection: collection_id,1720                    owner: new_owner.clone(),1721                    value: val64,1722                };17231724                Self::add_fungible_item(item)?;1725            }17261727            if amount == val64 {1728                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;17291730                // remove approve list1731                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1732                <FungibleItemList<T>>::remove(collection_id, item_id);1733            }17341735            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1736        }17371738        Ok(())1739    }17401741    fn transfer_refungible(1742        collection_id: u64,1743        item_id: u64,1744        value: u64,1745        owner: T::AccountId,1746        new_owner: T::AccountId,1747    ) -> DispatchResult {1748        ensure!(1749            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1750            Error::<T>::TokenNotFound1751        );17521753        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1754        let item = full_item1755            .owner1756            .iter()1757            .filter(|i| i.owner == owner)1758            .next()1759            .ok_or(Error::<T>::NumOverflow)?;1760        let amount = item.fraction;17611762        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);17631764        // update balance1765        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1766            .checked_sub(value)1767            .ok_or(Error::<T>::NumOverflow)?;1768        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);17691770        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1771            .checked_add(value)1772            .ok_or(Error::<T>::NumOverflow)?;1773        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17741775        let old_owner = item.owner.clone();1776        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1777        let val64 = value.into();17781779        // transfer1780        if amount == val64 && !new_owner_has_account {1781            // change owner1782            // new owner do not have account1783            let mut new_full_item = full_item.clone();1784            new_full_item1785                .owner1786                .iter_mut()1787                .find(|i| i.owner == owner)1788                .unwrap()1789                .owner = new_owner.clone();1790            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17911792            // update index collection1793            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1794        } else {1795            let mut new_full_item = full_item.clone();1796            new_full_item1797                .owner1798                .iter_mut()1799                .find(|i| i.owner == owner)1800                .unwrap()1801                .fraction -= val64;18021803            // separate amount1804            if new_owner_has_account {1805                // new owner has account1806                new_full_item1807                    .owner1808                    .iter_mut()1809                    .find(|i| i.owner == new_owner)1810                    .unwrap()1811                    .fraction += val64;1812            } else {1813                // new owner do not have account1814                new_full_item.owner.push(Ownership {1815                    owner: new_owner.clone(),1816                    fraction: val64,1817                });1818                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1819            }18201821            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1822        }18231824        Ok(())1825    }18261827    fn transfer_nft(1828        collection_id: u64,1829        item_id: u64,1830        sender: T::AccountId,1831        new_owner: T::AccountId,1832    ) -> DispatchResult {1833        ensure!(1834            <NftItemList<T>>::contains_key(collection_id, item_id),1835            Error::<T>::TokenNotFound1836        );18371838        let mut item = <NftItemList<T>>::get(collection_id, item_id);18391840        ensure!(1841            sender == item.owner,1842            Error::<T>::MustBeTokenOwner1843        );18441845        // update balance1846        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1847            .checked_sub(1)1848            .ok_or(Error::<T>::NumOverflow)?;1849        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18501851        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1852            .checked_add(1)1853            .ok_or(Error::<T>::NumOverflow)?;1854        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18551856        // change owner1857        let old_owner = item.owner.clone();1858        item.owner = new_owner.clone();1859        <NftItemList<T>>::insert(collection_id, item_id, item);18601861        // update index collection1862        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;18631864        // reset approved list1865        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1866        Ok(())1867    }1868    1869    fn item_exists(1870        collection_id: u64,1871        item_id: u64,1872        mode: &CollectionMode1873    ) -> DispatchResult {1874        match mode {1875            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1876            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1877            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1878            _ => ()1879        };1880        1881        Ok(())1882    }18831884    fn set_re_fungible_variable_data(1885        collection_id: u64,1886        item_id: u64,1887        data: Vec<u8>1888    ) -> DispatchResult {1889        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);18901891        item.variable_data = data;18921893        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);18941895        Ok(())1896    }18971898    fn set_nft_variable_data(1899        collection_id: u64,1900        item_id: u64,1901        data: Vec<u8>1902    ) -> DispatchResult {1903        let mut item = <NftItemList<T>>::get(collection_id, item_id);1904        1905        item.variable_data = data;19061907        <NftItemList<T>>::insert(collection_id, item_id, item);1908        1909        Ok(())1910    }19111912    fn init_collection(item: &CollectionType<T::AccountId>) {1913        // check params1914        assert!(1915            item.decimal_points <= 4,1916            "decimal_points parameter must be lower than 4"1917        );1918        assert!(1919            item.name.len() <= 64,1920            "Collection name can not be longer than 63 char"1921        );1922        assert!(1923            item.name.len() <= 256,1924            "Collection description can not be longer than 255 char"1925        );1926        assert!(1927            item.token_prefix.len() <= 16,1928            "Token prefix can not be longer than 15 char"1929        );19301931        // Generate next collection ID1932        let next_id = CreatedCollectionCount::get()1933            .checked_add(1)1934            .unwrap();19351936        CreatedCollectionCount::put(next_id);1937    }19381939    fn init_nft_token(item: &NftItemType<T::AccountId>) {1940        let current_index = <ItemListIndex>::get(item.collection)1941            .checked_add(1)1942            .unwrap();19431944        let item_owner = item.owner.clone();1945        let collection_id = item.collection.clone();1946        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();19471948        <ItemListIndex>::insert(collection_id, current_index);19491950        // Update balance1951        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1952            .checked_add(1)1953            .unwrap();1954        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1955    }19561957    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1958        let current_index = <ItemListIndex>::get(item.collection)1959            .checked_add(1)1960            .unwrap();1961        let owner = item.owner.clone();1962        let value = item.value as u64;19631964        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();19651966        <ItemListIndex>::insert(item.collection, current_index);19671968        // Update balance1969        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1970            .checked_add(value)1971            .unwrap();1972        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1973    }19741975    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1976        let current_index = <ItemListIndex>::get(item.collection)1977            .checked_add(1)1978            .unwrap();19791980        let value = item.owner.first().unwrap().fraction as u64;1981        let owner = item.owner.first().unwrap().owner.clone();19821983        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();19841985        <ItemListIndex>::insert(item.collection, current_index);19861987        // Update balance1988        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1989            .checked_add(value)1990            .unwrap();1991        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1992    }19931994    fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {19951996        // add to account limit1997        if <AccountItemCount<T>>::contains_key(owner.clone()) {19981999            // bound Owned tokens by a single address2000            let count = <AccountItemCount<T>>::get(owner.clone());2001            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);20022003            <AccountItemCount<T>>::insert(owner.clone(), count2004                .checked_add(1)2005                .ok_or(Error::<T>::NumOverflow)?);2006        }2007        else {2008            <AccountItemCount<T>>::insert(owner.clone(), 1);2009        }20102011        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2012        if list_exists {2013            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2014            let item_contains = list.contains(&item_index.clone());20152016            if !item_contains {2017                list.push(item_index.clone());2018            }20192020            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2021        } else {2022            let mut itm = Vec::new();2023            itm.push(item_index.clone());2024            <AddressTokens<T>>::insert(collection_id, owner, itm);2025            2026        }20272028        Ok(())2029    }20302031    fn remove_token_index(2032        collection_id: u64,2033        item_index: u64,2034        owner: T::AccountId,2035    ) -> DispatchResult {20362037        // update counter2038        <AccountItemCount<T>>::insert(owner.clone(), 2039            <AccountItemCount<T>>::get(owner.clone())2040            .checked_sub(1)2041            .ok_or(Error::<T>::NumOverflow)?);204220432044        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2045        if list_exists {2046            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2047            let item_contains = list.contains(&item_index.clone());20482049            if item_contains {2050                list.retain(|&item| item != item_index);2051                <AddressTokens<T>>::insert(collection_id, owner, list);2052            }2053        }20542055        Ok(())2056    }20572058    fn move_token_index(2059        collection_id: u64,2060        item_index: u64,2061        old_owner: T::AccountId,2062        new_owner: T::AccountId,2063    ) -> DispatchResult {2064        Self::remove_token_index(collection_id, item_index, old_owner)?;2065        Self::add_token_index(collection_id, item_index, new_owner)?;20662067        Ok(())2068    }2069}20702071////////////////////////////////////////////////////////////////////////////////////////////////////2072// Economic models2073// #region20742075/// Fee multiplier.2076pub type Multiplier = FixedU128;20772078type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2079    <T as system::Trait>::AccountId,2080>>::Balance;2081type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2082    <T as system::Trait>::AccountId,2083>>::NegativeImbalance;20842085/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2086/// in the queue.2087#[derive(Encode, Decode, Clone, Eq, PartialEq)]2088pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2089    #[codec(compact)] BalanceOf<T>2090);20912092impl<T: Trait + Send + Sync> sp_std::fmt::Debug2093    for ChargeTransactionPayment<T>2094{2095    #[cfg(feature = "std")]2096    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2097        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2098    }2099    #[cfg(not(feature = "std"))]2100    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2101        Ok(())2102    }2103}21042105impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2106where2107    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2108    BalanceOf<T>: Send + Sync + FixedPointOperand,2109{2110    /// utility constructor. Used only in client/factory code.2111    pub fn from(fee: BalanceOf<T>) -> Self {2112        Self(fee)2113    }21142115    pub fn traditional_fee(2116        len: usize,2117        info: &DispatchInfoOf<T::Call>,2118        tip: BalanceOf<T>,2119    ) -> BalanceOf<T>2120    where2121        T::Call: Dispatchable<Info = DispatchInfo>,2122    {2123        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2124    }21252126    fn withdraw_fee(2127        &self,2128        who: &T::AccountId,2129        call: &T::Call,2130        info: &DispatchInfoOf<T::Call>,2131        len: usize,2132    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2133        let tip = self.0;21342135        // Set fee based on call type. Creating collection costs 1 Unique.2136        // All other transactions have traditional fees so far2137        // let fee = match call.is_sub_type() {2138        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2139        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2140        //                                                 // _ => <BalanceOf<T>>::from(100)2141        // };2142        let fee = Self::traditional_fee(len, info, tip);21432144        // Determine who is paying transaction fee based on ecnomic model2145        // Parse call to extract collection ID and access collection sponsor2146        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2147            Some(Call::create_item(collection_id, _properties, _owner)) => {2148                <Collection<T>>::get(collection_id).sponsor2149            }2150            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2151                let _collection_mode = <Collection<T>>::get(collection_id).mode;21522153                // sponsor timeout2154                let sponsor_transfer = match _collection_mode {2155                    CollectionMode::NFT => {2156                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2157                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2158                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2159                        if block_number >= limit_time {2160                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2161                            true2162                        }2163                        else {2164                            false2165                        }2166                    }2167                    CollectionMode::Fungible(_) => {2168                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2169                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2170                        if basket.iter().any(|i| i.address == _new_owner.clone())2171                        {2172                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2173                            let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2174                            if block_number >= limit_time {2175                                basket.retain(|x| x.address == item.address);2176                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2177                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2178                                true2179                            }2180                            else {2181                                false2182                            }2183                        }2184                        else {2185                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2186                            true2187                        }2188                    }2189                    CollectionMode::ReFungible(_) => {2190                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2191                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2192                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2193                        if block_number >= limit_time {2194                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2195                            true2196                        } else {2197                            false2198                        }2199                    }2200                    _ => {2201                        false2202                    },2203                };22042205                if !sponsor_transfer {2206                    T::AccountId::default()2207                } else {2208                    <Collection<T>>::get(collection_id).sponsor2209                }2210            }22112212            _ => T::AccountId::default(),2213        };22142215        // Sponsor smart contracts2216        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {22172218            // On instantiation: set the contract owner2219            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {22202221                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2222                    code_hash,2223                    &data,2224                    &who,2225                );2226                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());22272228                T::AccountId::default()2229            },22302231            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2232            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {22332234                let mut sp = T::AccountId::default();2235                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());2236                if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2237                    if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2238                        sp = called_contract;2239                    }2240                }22412242                sp2243            },22442245            _ => sponsor,2246        };22472248        let mut who_pays_fee: T::AccountId = sponsor.clone();2249        if sponsor == T::AccountId::default() {2250            who_pays_fee = who.clone();2251        }22522253        // Only mess with balances if fee is not zero.2254        if fee.is_zero() {2255            return Ok((fee, None));2256        }22572258        match <T as transaction_payment::Trait>::Currency::withdraw(2259            &who_pays_fee,2260            fee,2261            if tip.is_zero() {2262                WithdrawReason::TransactionPayment.into()2263            } else {2264                WithdrawReason::TransactionPayment | WithdrawReason::Tip2265            },2266            ExistenceRequirement::KeepAlive,2267        ) {2268            Ok(imbalance) => Ok((fee, Some(imbalance))),2269            Err(_) => Err(InvalidTransaction::Payment.into()),2270        }2271    }2272}227322742275impl<T: Trait + Send + Sync> SignedExtension2276    for ChargeTransactionPayment<T>2277where2278    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2279    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2280{2281    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2282    type AccountId = T::AccountId;2283    type Call = T::Call;2284    type AdditionalSigned = ();2285    type Pre = (2286        BalanceOf<T>,2287        Self::AccountId,2288        Option<NegativeImbalanceOf<T>>,2289        BalanceOf<T>,2290    );2291    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2292        Ok(())2293    }22942295    fn validate(2296        &self,2297        _who: &Self::AccountId,2298        _call: &Self::Call,2299        _info: &DispatchInfoOf<Self::Call>,2300        _len: usize,2301    ) -> TransactionValidity {2302        Ok(ValidTransaction::default())2303    }23042305    fn pre_dispatch(2306        self,2307        who: &Self::AccountId,2308        call: &Self::Call,2309        info: &DispatchInfoOf<Self::Call>,2310        len: usize,2311    ) -> Result<Self::Pre, TransactionValidityError> {2312        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2313        Ok((self.0, who.clone(), imbalance, fee))2314    }23152316    fn post_dispatch(2317        pre: Self::Pre,2318        info: &DispatchInfoOf<Self::Call>,2319        post_info: &PostDispatchInfoOf<Self::Call>,2320        len: usize,2321        _result: &DispatchResult,2322    ) -> Result<(), TransactionValidityError> {2323        let (tip, who, imbalance, fee) = pre;2324        if let Some(payed) = imbalance {2325            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2326                len as u32, info, post_info, tip,2327            );2328            let refund = fee.saturating_sub(actual_fee);2329            let actual_payment =2330                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2331                    &who, refund,2332                ) {2333                    Ok(refund_imbalance) => {2334                        // The refund cannot be larger than the up front payed max weight.2335                        // `PostDispatchInfo::calc_unspent` guards against such a case.2336                        match payed.offset(refund_imbalance) {2337                            Ok(actual_payment) => actual_payment,2338                            Err(_) => return Err(InvalidTransaction::Payment.into()),2339                        }2340                    }2341                    // We do not recreate the account using the refund. The up front payment2342                    // is gone in that case.2343                    Err(_) => payed,2344                };2345            let imbalances = actual_payment.split(tip);2346            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2347                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2348            );2349        }2350        Ok(())2351    }2352}23532354// #endregion
modifiedpallets/nft/src/tests.rsdiffbeforeafterboth
--- a/pallets/nft/src/tests.rs
+++ b/pallets/nft/src/tests.rs
@@ -87,6 +87,32 @@
     });
 }
 
+// Use cases tests region
+// #region
+#[test]
+fn create_nft_multiple_items() {
+    new_test_ext().execute_with(|| {
+        default_limits();
+        
+        create_test_collection(&CollectionMode::NFT, 1);
+
+        let origin1 = Origin::signed(1);
+
+        let items_data = vec![default_nft_data(), default_nft_data(), default_nft_data()];
+
+        assert_ok!(TemplateModule::create_multiple_items(
+            origin1.clone(),
+            1,
+            1,
+            items_data.clone().into_iter().map(|d| { d.into() }).collect()
+        ));
+        for (index, data) in items_data.iter().enumerate() {
+            assert_eq!(TemplateModule::nft_item_id(1, (index + 1) as u64).const_data.to_vec(), data.const_data);
+            assert_eq!(TemplateModule::nft_item_id(1, (index + 1) as u64).variable_data.to_vec(), data.variable_data);
+        }
+    });
+}
+
 #[test]
 fn create_refungible_item() {
     new_test_ext().execute_with(|| {
@@ -114,6 +140,39 @@
 }
 
 #[test]
+fn create_multiple_refungible_items() {
+    new_test_ext().execute_with(|| {
+        default_limits();
+        
+        create_test_collection(&CollectionMode::ReFungible(3), 1);
+
+        let origin1 = Origin::signed(1);
+
+        let items_data = vec![default_re_fungible_data(), default_re_fungible_data(), default_re_fungible_data()];
+
+        assert_ok!(TemplateModule::create_multiple_items(
+            origin1.clone(),
+            1,
+            1,
+            items_data.clone().into_iter().map(|d| { d.into() }).collect()
+        ));
+        for (index, data) in items_data.iter().enumerate() {
+
+            let item = TemplateModule::refungible_item_id(1, (index + 1) as u64);
+            assert_eq!(item.const_data.to_vec(), data.const_data);
+            assert_eq!(item.variable_data.to_vec(), data.variable_data);
+            assert_eq!(
+                item.owner[0],
+                Ownership {
+                    owner: 1,
+                    fraction: 1000
+                }
+            );
+        }
+    });
+}
+
+#[test]
 fn create_fungible_item() {
     new_test_ext().execute_with(|| {
         default_limits();
@@ -124,12 +183,36 @@
         create_test_item(collection_id, &data.into());
 
         assert_eq!(TemplateModule::fungible_item_id(collection_id, 1).owner, 1);
-        assert_eq!(TemplateModule::balance_count(1, 1), 1000);
-        assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
     });
 }
 
 #[test]
+fn create_multiple_fungible_items() {
+    new_test_ext().execute_with(|| {
+        default_limits();
+
+        create_test_collection(&CollectionMode::Fungible(3), 1);
+
+        let origin1 = Origin::signed(1);
+
+        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()
+        ));
+        
+        for (index, _) in items_data.iter().enumerate() {
+            assert_eq!(TemplateModule::fungible_item_id(1, (index + 1) as u64).owner, 1);
+        }
+        assert_eq!(TemplateModule::balance_count(1, 1), 3000);
+        assert_eq!(TemplateModule::address_tokens(1, 1), [1, 2, 3]);
+    });
+}
+
+#[test]
 fn transfer_fungible_item() {
     new_test_ext().execute_with(|| {
         default_limits();
@@ -1333,7 +1416,7 @@
 
         assert_noop!(
             TemplateModule::create_item(origin2.clone(), 1, 2, default_nft_data().into()),
-            "Public minting is not allowed for this collection."
+            "Public minting is not allowed for this collection"
         );
     });
 }
@@ -1362,7 +1445,7 @@
 
         assert_noop!(
             TemplateModule::create_item(origin2.clone(), 1, 2, default_nft_data().into()),
-            "Public minting is not allowed for this collection."
+            "Public minting is not allowed for this collection"
         );
     });
 }
modifiedruntime/src/nft_weights.rsdiffbeforeafterboth
--- a/runtime/src/nft_weights.rs
+++ b/runtime/src/nft_weights.rs
@@ -64,7 +64,7 @@
     }  
     fn create_item(s: usize, ) -> Weight {
         (130_000_000 as Weight)
-            .saturating_add((2135 as Weight).saturating_mul(s as Weight))
+            .saturating_add((2135 as Weight).saturating_mul(s as Weight).saturating_mul(500 as Weight)) // 500 is temparary multiplier, fee for storage
             .saturating_add(DbWeight::get().reads(10 as Weight))
             .saturating_add(DbWeight::get().writes(8 as Weight))
     }