git.delta.rocks / unique-network / refs/commits / 7ff84981c059

difftreelog

Merge branch 'develop' into feature/NFTPAR-142

sotmorskiy2020-12-03parents: #d4ad9e6 #82ca9c1.patch.diff
in: master
# Conflicts:
#	pallets/nft/src/lib.rs

5 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3737,7 +3737,10 @@
  "frame-support",
  "frame-system",
  "log",
+ "pallet-balances",
  "pallet-contracts",
+ "pallet-randomness-collective-flip",
+ "pallet-timestamp",
  "pallet-transaction-payment",
  "parity-scale-codec",
  "serde",
modifiedpallets/nft/src/default_weights.rsdiffbeforeafterboth
--- a/pallets/nft/src/default_weights.rs
+++ b/pallets/nft/src/default_weights.rs
@@ -107,9 +107,19 @@
             .saturating_add(DbWeight::get().reads(2 as Weight))
             .saturating_add(DbWeight::get().writes(1 as Weight))
     }
+    // fn set_chain_limits() -> Weight {
+    //     (0 as Weight)
+    //         .saturating_add(DbWeight::get().reads(1 as Weight))
+    //         .saturating_add(DbWeight::get().writes(1 as Weight))
+    // }
     // fn enable_contract_sponsoring() -> Weight {
     //     (0 as Weight)
     //         .saturating_add(DbWeight::get().reads(1 as Weight))
     //         .saturating_add(DbWeight::get().writes(1 as Weight))
     // }
+    // fn set_contract_sponsoring_rate_limit() -> Weight {
+    //     (0 as Weight)
+    //         .saturating_add(DbWeight::get().reads(1 as Weight))
+    //         .saturating_add(DbWeight::get().writes(1 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, 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 exceeded.268        TotalCollectionsLimitExceeded,269		/// Decimal_points parameter must be lower than 4.270        CollectionDecimalPointLimitExceeded, 271        /// Collection name can not be longer than 63 char.272        CollectionNameLimitExceeded, 273        /// Collection description can not be longer than 255 char.274        CollectionDescriptionLimitExceeded, 275        /// Token prefix can not be longer than 15 char.276        CollectionTokenPrefixLimitExceeded,277        /// This collection does not exist.278        CollectionNotFound,279        /// Item not exists.280        TokenNotFound,281        /// Arithmetic calculation overflow.282        NumOverflow,       283        /// Account already has admin role.284        AlreadyAdmin,  285        /// You do not own this collection.286        NoPermission,287        /// This address is not set as sponsor, use setCollectionSponsor first.288        ConfirmUnsetSponsorFail,289        /// Collection is not in mint mode.290        PublicMintingNotAllowed,291        /// Sender parameter and item owner must be equal.292        MustBeTokenOwner,293        /// Item balance not enough.294        TokenValueTooLow,295        /// Size of item is too large.296        NftSizeLimitExceeded,297        /// No approve found298        ApproveNotFound,299        /// Requested value more than approved.300        TokenValueNotEnough,301        /// Only approved addresses can call this method.302        ApproveRequired,303        /// Address is not in white list.304        AddresNotInWhiteList,305        /// Number of collection admins bound exceeded.306        CollectionAdminsLimitExceeded,307        /// Owned tokens by a single address bound exceeded.308        AddressOwnershipLimitExceeded,309        /// Length of items properties must be greater than 0.310        EmptyArgument,311        /// const_data exceeded data limit.312        TokenConstDataLimitExceeded,313        /// variable_data exceeded data limit.314        TokenVariableDataLimitExceeded,315        /// Not NFT item data used to mint in NFT collection.316        NotNftDataUsedToMintNftCollectionToken,317        /// Not Fungible item data used to mint in Fungible collection.318        NotFungibleDataUsedToMintFungibleCollectionToken,319        /// Not Re Fungible item data used to mint in Re Fungible collection.320        NotReFungibleDataUsedToMintReFungibleCollectionToken,321        /// Unexpected collection type.322        UnexpectedCollectionType,323        /// Can't store metadata in fungible tokens.324        CantSotreMetadataInFungibleTokens325	}326}327328pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {329    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;330331    /// Weight information for extrinsics in this pallet.332	type WeightInfo: WeightInfo;333}334335#[cfg(feature = "runtime-benchmarks")]336mod benchmarking;337338// #endregion339340decl_storage! {341    trait Store for Module<T: Trait> as Nft {342343        // Private members344        NextCollectionID: u64;345        CreatedCollectionCount: u64;346        ChainVersion: u64;347        ItemListIndex: map hasher(blake2_128_concat) u64 => u64;348349        // Chain limits struct350        pub ChainLimit get(fn chain_limit) config(): ChainLimits;351352        // Bound counters353        CollectionCount: u64;354        pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;355356        // Basic collections357        pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;358        pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;359        pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;360361        /// Balance owner per collection map362        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;363364        /// second parameter: item id + owner account id365        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;366367        /// Item collections368        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;369        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;370        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;371372        /// Index list373        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;374375        /// Tokens transfer baskets376        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;377        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>>;378        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;379380        // Contract Sponsorship and Ownership381        pub ContractOwner get(fn contract_owner): map hasher(identity) T::AccountId => T::AccountId;382        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(identity) T::AccountId => bool;383    }384    add_extra_genesis {385        build(|config: &GenesisConfig<T>| {386            // Modification of storage387            for (_num, _c) in &config.collection {388                <Module<T>>::init_collection(_c);389            }390391            for (_num, _q, _i) in &config.nft_item_id {392                <Module<T>>::init_nft_token(_i);393            }394395            for (_num, _q, _i) in &config.fungible_item_id {396                <Module<T>>::init_fungible_token(_i);397            }398399            for (_num, _q, _i) in &config.refungible_item_id {400                <Module<T>>::init_refungible_token(_i);401            }402        })403    }404}405406decl_event!(407    pub enum Event<T>408    where409        AccountId = <T as system::Trait>::AccountId,410    {411        /// New collection was created412        /// 413        /// # Arguments414        /// 415        /// * collection_id: Globally unique identifier of newly created collection.416        /// 417        /// * mode: [CollectionMode] converted into u8.418        /// 419        /// * account_id: Collection owner.420        Created(u64, u8, AccountId),421422        /// New item was created.423        /// 424        /// # Arguments425        /// 426        /// * collection_id: Id of the collection where item was created.427        /// 428        /// * item_id: Id of an item. Unique within the collection.429        ItemCreated(u64, u64),430431        /// Collection item was burned.432        /// 433        /// # Arguments434        /// 435        /// collection_id.436        /// 437        /// item_id: Identifier of burned NFT.438        ItemDestroyed(u64, u64),439    }440);441442decl_module! {443    pub struct Module<T: Trait> for enum Call where origin: T::Origin {444445        fn deposit_event() = default;446        type Error = Error<T>;447448        fn on_initialize(now: T::BlockNumber) -> Weight {449450            if ChainVersion::get() < 2451            {452                let value = NextCollectionID::get();453                CreatedCollectionCount::put(value);454                ChainVersion::put(2);455            }456457            0458        }459460        /// 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.461        /// 462        /// # Permissions463        /// 464        /// * Anyone.465        /// 466        /// # Arguments467        /// 468        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.469        /// 470        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.471        /// 472        /// * token_prefix: UTF-8 string with token prefix.473        /// 474        /// * mode: [CollectionMode] collection type and type dependent data.475        // returns collection ID476        #[weight = T::WeightInfo::create_collection()]477        pub fn create_collection(origin,478                                 collection_name: Vec<u16>,479                                 collection_description: Vec<u16>,480                                 token_prefix: Vec<u8>,481                                 mode: CollectionMode) -> DispatchResult {482483            // Anyone can create a collection484            let who = ensure_signed(origin)?;485486            let decimal_points = match mode {487                CollectionMode::Fungible(points) => points,488                CollectionMode::ReFungible(points) => points,489                _ => 0490            };491492            // bound Total number of collections493            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);494495            // check params496            ensure!(decimal_points <= 4, Error::<T>::CollectionDecimalPointLimitExceeded);497498            let mut name = collection_name.to_vec();499            name.push(0);500            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);501502            let mut description = collection_description.to_vec();503            description.push(0);504            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);505506            let mut prefix = token_prefix.to_vec();507            prefix.push(0);508            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);509510            // Generate next collection ID511            let next_id = CreatedCollectionCount::get()512                .checked_add(1)513                .ok_or(Error::<T>::NumOverflow)?;514515            // bound counter516            let total = CollectionCount::get()517                .checked_add(1)518                .ok_or(Error::<T>::NumOverflow)?;519520            CreatedCollectionCount::put(next_id);521            CollectionCount::put(total);522523            // Create new collection524            let new_collection = CollectionType {525                owner: who.clone(),526                name: name,527                mode: mode.clone(),528                mint_mode: false,529                access: AccessMode::Normal,530                description: description,531                decimal_points: decimal_points,532                token_prefix: prefix,533                offchain_schema: Vec::new(),534                sponsor: T::AccountId::default(),535                unconfirmed_sponsor: T::AccountId::default(),536                variable_on_chain_schema: Vec::new(),537                const_on_chain_schema: Vec::new(),538            };539540            // Add new collection to map541            <Collection<T>>::insert(next_id, new_collection);542543            // call event544            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));545546            Ok(())547        }548549        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.550        /// 551        /// # Permissions552        /// 553        /// * Collection Owner.554        /// 555        /// # Arguments556        /// 557        /// * collection_id: collection to destroy.558        #[weight = T::WeightInfo::destroy_collection()]559        pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {560561            let sender = ensure_signed(origin)?;562            Self::check_owner_permissions(collection_id, sender)?;563564            <AddressTokens<T>>::remove_prefix(collection_id);565            <ApprovedList<T>>::remove_prefix(collection_id);566            <Balance<T>>::remove_prefix(collection_id);567            <ItemListIndex>::remove(collection_id);568            <AdminList<T>>::remove(collection_id);569            <Collection<T>>::remove(collection_id);570            <WhiteList<T>>::remove(collection_id);571572            <NftItemList<T>>::remove_prefix(collection_id);573            <FungibleItemList<T>>::remove_prefix(collection_id);574            <ReFungibleItemList<T>>::remove_prefix(collection_id);575576            <NftTransferBasket<T>>::remove_prefix(collection_id);577            <FungibleTransferBasket<T>>::remove_prefix(collection_id);578            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);579580            if CollectionCount::get() > 0581            {582                // bound couter583                let total = CollectionCount::get()584                    .checked_sub(1)585                    .ok_or(Error::<T>::NumOverflow)?;586587                CollectionCount::put(total);588            }589590            Ok(())591        }592593        /// Add an address to white list.594        /// 595        /// # Permissions596        /// 597        /// * Collection Owner598        /// * Collection Admin599        /// 600        /// # Arguments601        /// 602        /// * collection_id.603        /// 604        /// * address.605        #[weight = T::WeightInfo::add_to_white_list()]606        pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{607608            let sender = ensure_signed(origin)?;609            Self::check_owner_or_admin_permissions(collection_id, sender)?;610611            let mut white_list_collection: Vec<T::AccountId>;612            if <WhiteList<T>>::contains_key(collection_id) {613                white_list_collection = <WhiteList<T>>::get(collection_id);614                if !white_list_collection.contains(&address.clone())615                {616                    white_list_collection.push(address.clone());617                }618            }619            else {620                white_list_collection = Vec::new();621                white_list_collection.push(address.clone());622            }623624            <WhiteList<T>>::insert(collection_id, white_list_collection);625            Ok(())626        }627628        /// Remove an address from white list.629        /// 630        /// # Permissions631        /// 632        /// * Collection Owner633        /// * Collection Admin634        /// 635        /// # Arguments636        /// 637        /// * collection_id.638        /// 639        /// * address.640        #[weight = T::WeightInfo::remove_from_white_list()]641        pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{642643            let sender = ensure_signed(origin)?;644            Self::check_owner_or_admin_permissions(collection_id, sender)?;645646            if <WhiteList<T>>::contains_key(collection_id) {647                let mut white_list_collection = <WhiteList<T>>::get(collection_id);648                if white_list_collection.contains(&address.clone())649                {650                    white_list_collection.retain(|i| *i != address.clone());651                    <WhiteList<T>>::insert(collection_id, white_list_collection);652                }653            }654655            Ok(())656        }657658        /// Toggle between normal and white list access for the methods with access for `Anyone`.659        /// 660        /// # Permissions661        /// 662        /// * Collection Owner.663        /// 664        /// # Arguments665        /// 666        /// * collection_id.667        /// 668        /// * mode: [AccessMode]669        #[weight = T::WeightInfo::set_public_access_mode()]670        pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult671        {672            let sender = ensure_signed(origin)?;673674            Self::check_owner_permissions(collection_id, sender)?;675            let mut target_collection = <Collection<T>>::get(collection_id);676            target_collection.access = mode;677            <Collection<T>>::insert(collection_id, target_collection);678679            Ok(())680        }681682        /// Allows Anyone to create tokens if:683        /// * White List is enabled, and684        /// * Address is added to white list, and685        /// * This method was called with True parameter686        /// 687        /// # Permissions688        /// * Collection Owner689        ///690        /// # Arguments691        /// 692        /// * collection_id.693        /// 694        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.695        #[weight = T::WeightInfo::set_mint_permission()]696        pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult697        {698            let sender = ensure_signed(origin)?;699700            Self::check_owner_permissions(collection_id, sender)?;701            let mut target_collection = <Collection<T>>::get(collection_id);702            target_collection.mint_mode = mint_permission;703            <Collection<T>>::insert(collection_id, target_collection);704705            Ok(())706        }707708        /// Change the owner of the collection.709        /// 710        /// # Permissions711        /// 712        /// * Collection Owner.713        /// 714        /// # Arguments715        /// 716        /// * collection_id.717        /// 718        /// * new_owner.719        #[weight = T::WeightInfo::change_collection_owner()]720        pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {721722            let sender = ensure_signed(origin)?;723            Self::check_owner_permissions(collection_id, sender)?;724            let mut target_collection = <Collection<T>>::get(collection_id);725            target_collection.owner = new_owner;726            <Collection<T>>::insert(collection_id, target_collection);727728            Ok(())729        }730731        /// Adds an admin of the Collection.732        /// 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. 733        /// 734        /// # Permissions735        /// 736        /// * Collection Owner.737        /// * Collection Admin.738        /// 739        /// # Arguments740        /// 741        /// * collection_id: ID of the Collection to add admin for.742        /// 743        /// * new_admin_id: Address of new admin to add.744        #[weight = T::WeightInfo::add_collection_admin()]745        pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {746747            let sender = ensure_signed(origin)?;748            Self::check_owner_or_admin_permissions(collection_id, sender)?;749            let mut admin_arr: Vec<T::AccountId> = Vec::new();750751            if <AdminList<T>>::contains_key(collection_id)752            {753                admin_arr = <AdminList<T>>::get(collection_id);754                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);755            }756757            // Number of collection admins758            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);759760            admin_arr.push(new_admin_id);761            <AdminList<T>>::insert(collection_id, admin_arr);762763            Ok(())764        }765766        /// 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.767        ///768        /// # Permissions769        /// 770        /// * Collection Owner.771        /// * Collection Admin.772        /// 773        /// # Arguments774        /// 775        /// * collection_id: ID of the Collection to remove admin for.776        /// 777        /// * account_id: Address of admin to remove.778        #[weight = T::WeightInfo::remove_collection_admin()]779        pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {780781            let sender = ensure_signed(origin)?;782            Self::check_owner_or_admin_permissions(collection_id, sender)?;783784            if <AdminList<T>>::contains_key(collection_id)785            {786                let mut admin_arr = <AdminList<T>>::get(collection_id);787                admin_arr.retain(|i| *i != account_id);788                <AdminList<T>>::insert(collection_id, admin_arr);789            }790791            Ok(())792        }793794        /// # Permissions795        /// 796        /// * Collection Owner797        /// 798        /// # Arguments799        /// 800        /// * collection_id.801        /// 802        /// * new_sponsor.803        #[weight = T::WeightInfo::set_collection_sponsor()]804        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {805806            let sender = ensure_signed(origin)?;807            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);808809            let mut target_collection = <Collection<T>>::get(collection_id);810            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);811812            target_collection.unconfirmed_sponsor = new_sponsor;813            <Collection<T>>::insert(collection_id, target_collection);814815            Ok(())816        }817818        /// # Permissions819        /// 820        /// * Sponsor.821        /// 822        /// # Arguments823        /// 824        /// * collection_id.825        #[weight = T::WeightInfo::confirm_sponsorship()]826        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {827828            let sender = ensure_signed(origin)?;829            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);830831            let mut target_collection = <Collection<T>>::get(collection_id);832            ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);833834            target_collection.sponsor = target_collection.unconfirmed_sponsor;835            target_collection.unconfirmed_sponsor = T::AccountId::default();836            <Collection<T>>::insert(collection_id, target_collection);837838            Ok(())839        }840841        /// Switch back to pay-per-own-transaction model.842        ///843        /// # Permissions844        ///845        /// * Collection owner.846        /// 847        /// # Arguments848        /// 849        /// * collection_id.850        #[weight = T::WeightInfo::remove_collection_sponsor()]851        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {852853            let sender = ensure_signed(origin)?;854            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);855856            let mut target_collection = <Collection<T>>::get(collection_id);857            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);858859            target_collection.sponsor = T::AccountId::default();860            <Collection<T>>::insert(collection_id, target_collection);861862            Ok(())863        }864865        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.866        /// 867        /// # Permissions868        /// 869        /// * Collection Owner.870        /// * Collection Admin.871        /// * Anyone if872        ///     * White List is enabled, and873        ///     * Address is added to white list, and874        ///     * MintPermission is enabled (see SetMintPermission method)875        /// 876        /// # Arguments877        /// 878        /// * collection_id: ID of the collection.879        /// 880        /// * owner: Address, initial owner of the NFT.881        ///882        /// * data: Token data to store on chain.883        // #[weight =884        // (130_000_000 as Weight)885        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))886        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))887        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]888889        #[weight = T::WeightInfo::create_item(data.len())]890        pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {891892            let sender = ensure_signed(origin)?;893894            Self::collection_exists(collection_id)?;895896            let target_collection = <Collection<T>>::get(collection_id);897898            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;899            Self::validate_create_item_args(&target_collection, &data)?;900            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;901902            Ok(())903        }904905        /// This method creates multiple instances of NFT Collection created with CreateCollection method.906        /// 907        /// # Permissions908        /// 909        /// * Collection Owner.910        /// * Collection Admin.911        /// * Anyone if912        ///     * White List is enabled, and913        ///     * Address is added to white list, and914        ///     * MintPermission is enabled (see SetMintPermission method)915        /// 916        /// # Arguments917        /// 918        /// * collection_id: ID of the collection.919        /// 920        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].921        /// 922        /// * owner: Address, initial owner of the NFT.923        #[weight = T::WeightInfo::create_item(items_data.into_iter()924                               .map(|data| { data.len() })925                               .sum())]926        pub fn create_multiple_items(origin, collection_id: u64, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {927928            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);929            let sender = ensure_signed(origin)?;930931            Self::collection_exists(collection_id)?;932            let target_collection = <Collection<T>>::get(collection_id);933934            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;935936            for data in &items_data {937                Self::validate_create_item_args(&target_collection, data)?;938            }939            for data in &items_data {940                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;941            }942943            Ok(())944        }945946        /// Destroys a concrete instance of NFT.947        /// 948        /// # Permissions949        /// 950        /// * Collection Owner.951        /// * Collection Admin.952        /// * Current NFT Owner.953        /// 954        /// # Arguments955        /// 956        /// * collection_id: ID of the collection.957        /// 958        /// * item_id: ID of NFT to burn.959        #[weight = T::WeightInfo::burn_item()]960        pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {961962            let sender = ensure_signed(origin)?;963            Self::collection_exists(collection_id)?;964965            // Transfer permissions check966            let target_collection = <Collection<T>>::get(collection_id);967            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||968                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),969                Error::<T>::NoPermission);970971            if target_collection.access == AccessMode::WhiteList {972                Self::check_white_list(collection_id, &sender)?;973            }974975            match target_collection.mode976            {977                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,978                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,979                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,980                _ => ()981            };982983            // call event984            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));985986            Ok(())987        }988989        /// Change ownership of the token.990        /// 991        /// # Permissions992        /// 993        /// * Collection Owner994        /// * Collection Admin995        /// * Current NFT owner996        ///997        /// # Arguments998        /// 999        /// * recipient: Address of token recipient.1000        /// 1001        /// * collection_id.1002        /// 1003        /// * item_id: ID of the item1004        ///     * Non-Fungible Mode: Required.1005        ///     * Fungible Mode: Ignored.1006        ///     * Re-Fungible Mode: Required.1007        /// 1008        /// * value: Amount to transfer.1009        ///     * Non-Fungible Mode: Ignored1010        ///     * Fungible Mode: Must specify transferred amount1011        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1012        #[weight = T::WeightInfo::transfer()]1013        pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {10141015            let sender = ensure_signed(origin)?;10161017            // Transfer permissions check1018            let target_collection = <Collection<T>>::get(collection_id);1019            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1020                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1021                Error::<T>::NoPermission);10221023            if target_collection.access == AccessMode::WhiteList {1024                Self::check_white_list(collection_id, &sender)?;1025                Self::check_white_list(collection_id, &recipient)?;1026            }10271028            match target_collection.mode1029            {1030                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1031                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1032                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1033                _ => ()1034            };10351036            Ok(())1037        }10381039        /// Set, change, or remove approved address to transfer the ownership of the NFT.1040        /// 1041        /// # Permissions1042        /// 1043        /// * Collection Owner1044        /// * Collection Admin1045        /// * Current NFT owner1046        /// 1047        /// # Arguments1048        /// 1049        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1050        /// 1051        /// * collection_id.1052        /// 1053        /// * item_id: ID of the item.1054        #[weight = T::WeightInfo::approve()]1055        pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {10561057            let sender = ensure_signed(origin)?;10581059            // Transfer permissions check1060            let target_collection = <Collection<T>>::get(collection_id);1061            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1062                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1063                Error::<T>::NoPermission);10641065            if target_collection.access == AccessMode::WhiteList {1066                Self::check_white_list(collection_id, &sender)?;1067                Self::check_white_list(collection_id, &approved)?;1068            }10691070            // amount param stub1071            let amount = 100000000;10721073            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1074            if list_exists {10751076                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1077                let item_contains = list.iter().any(|i| i.approved == approved);10781079                if !item_contains {1080                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1081                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1082                }1083            } else {10841085                let mut list = Vec::new();1086                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1087                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1088            }10891090            Ok(())1091        }1092        1093        /// 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.1094        /// 1095        /// # Permissions1096        /// * Collection Owner1097        /// * Collection Admin1098        /// * Current NFT owner1099        /// * Address approved by current NFT owner1100        /// 1101        /// # Arguments1102        /// 1103        /// * from: Address that owns token.1104        /// 1105        /// * recipient: Address of token recipient.1106        /// 1107        /// * collection_id.1108        /// 1109        /// * item_id: ID of the item.1110        /// 1111        /// * value: Amount to transfer.1112        #[weight = T::WeightInfo::transfer_from()]1113        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {11141115            let sender = ensure_signed(origin)?;1116            let mut appoved_transfer = false;11171118            // Check approve1119            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1120                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1121                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1122                if opt_item.is_some()1123                {1124                    appoved_transfer = true;1125                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1126                }1127            }11281129            // Transfer permissions check1130            let target_collection = <Collection<T>>::get(collection_id);1131                ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1132                Error::<T>::NoPermission);11331134            if target_collection.access == AccessMode::WhiteList {1135                Self::check_white_list(collection_id, &sender)?;1136                Self::check_white_list(collection_id, &recipient)?;1137            }11381139            // remove approve1140            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1141                .into_iter().filter(|i| i.approved != sender.clone()).collect();1142            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);114311441145            match target_collection.mode1146            {1147                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1148                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1149                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1150                _ => ()1151            };11521153            Ok(())1154        }11551156        ///1157        #[weight = 0]1158        pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11591160            // let no_perm_mes = "You do not have permissions to modify this collection";1161            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1162            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1163            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11641165            // // on_nft_received  call11661167            // Self::transfer(origin, collection_id, item_id, new_owner)?;11681169            Ok(())1170        }11711172        /// Set off-chain data schema.1173        /// 1174        /// # Permissions1175        /// 1176        /// * Collection Owner1177        /// * Collection Admin1178        /// 1179        /// # Arguments1180        /// 1181        /// * collection_id.1182        /// 1183        /// * schema: String representing the offchain data schema.1184        #[weight = T::WeightInfo::set_variable_meta_data()]1185        pub fn set_variable_meta_data (1186            origin,1187            collection_id: u64,1188            item_id: u64,1189            data: Vec<u8>1190        ) -> DispatchResult {1191            let sender = ensure_signed(origin)?;1192            1193            Self::collection_exists(collection_id)?;11941195            // Modify permissions check1196            let target_collection = <Collection<T>>::get(collection_id);1197            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1198                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1199                Error::<T>::NoPermission);12001201            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12021203            match target_collection.mode1204            {1205                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1206                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1207                CollectionMode::Fungible(_) => fail!(Error::<T>::CantSotreMetadataInFungibleTokens),1208                _ => fail!(Error::<T>::UnexpectedCollectionType)1209            };12101211            Ok(())1212        }1213        12141215        /// Set off-chain data schema.1216        /// 1217        /// # Permissions1218        /// 1219        /// * Collection Owner1220        /// * Collection Admin1221        /// 1222        /// # Arguments1223        /// 1224        /// * collection_id.1225        /// 1226        /// * schema: String representing the offchain data schema.1227        #[weight = T::WeightInfo::set_offchain_schema()]1228        pub fn set_offchain_schema(1229            origin,1230            collection_id: u64,1231            schema: Vec<u8>1232        ) -> DispatchResult {1233            let sender = ensure_signed(origin)?;1234            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12351236            let mut target_collection = <Collection<T>>::get(collection_id);1237            target_collection.offchain_schema = schema;1238            <Collection<T>>::insert(collection_id, target_collection);12391240            Ok(())1241        }12421243        /// Set const on-chain data schema.1244        /// 1245        /// # Permissions1246        /// 1247        /// * Collection Owner1248        /// * Collection Admin1249        /// 1250        /// # Arguments1251        /// 1252        /// * collection_id.1253        /// 1254        /// * schema: String representing the const on-chain data schema.1255        #[weight = T::WeightInfo::set_const_on_chain_schema()]1256        pub fn set_const_on_chain_schema (1257            origin,1258            collection_id: u64,1259            schema: Vec<u8>1260        ) -> DispatchResult {1261            let sender = ensure_signed(origin)?;1262            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12631264            let mut target_collection = <Collection<T>>::get(collection_id);1265            target_collection.const_on_chain_schema = schema;1266            <Collection<T>>::insert(collection_id, target_collection);12671268            Ok(())1269        }12701271        /// Set variable on-chain data schema.1272        /// 1273        /// # Permissions1274        /// 1275        /// * Collection Owner1276        /// * Collection Admin1277        /// 1278        /// # Arguments1279        /// 1280        /// * collection_id.1281        /// 1282        /// * schema: String representing the variable on-chain data schema.1283        #[weight = T::WeightInfo::set_const_on_chain_schema()]1284        pub fn set_variable_on_chain_schema (1285            origin,1286            collection_id: u64,1287            schema: Vec<u8>1288        ) -> DispatchResult {1289            let sender = ensure_signed(origin)?;1290            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12911292            let mut target_collection = <Collection<T>>::get(collection_id);1293            target_collection.variable_on_chain_schema = schema;1294            <Collection<T>>::insert(collection_id, target_collection);12951296            Ok(())1297        }12981299        // Sudo permissions function1300        #[weight = 0]1301        pub fn set_chain_limits(1302            origin,1303            limits: ChainLimits1304        ) -> DispatchResult {1305            ensure_root(origin)?;1306            <ChainLimit>::put(limits);1307            Ok(())1308        }13091310        /// Enable smart contract self-sponsoring.1311        /// 1312        /// # Permissions1313        /// 1314        /// * Contract Owner1315        /// 1316        /// # Arguments1317        /// 1318        /// * contract address1319        /// * enable flag1320        /// 1321        #[weight = 0]1322        pub fn enable_contract_sponsoring(1323            origin,1324            contract_address: T::AccountId,1325            enable: bool1326        ) -> DispatchResult {1327            let sender = ensure_signed(origin)?;1328            let mut is_owner = false;1329            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1330                let owner = <ContractOwner<T>>::get(&contract_address);1331                is_owner = sender == owner;1332            }1333            ensure!(is_owner, Error::<T>::NoPermission);13341335            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1336            Ok(())1337        }1338    }1339}13401341impl<T: Trait> Module<T> {13421343    fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {13441345        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1346            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1347            Self::check_white_list(collection_id, owner)?;1348            Self::check_white_list(collection_id, sender)?;1349        }13501351        Ok(())1352    }13531354    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1355        match target_collection.mode1356        {1357            CollectionMode::NFT => {1358                if let CreateItemData::NFT(data) = data {1359                    // check sizes1360                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1361                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1362                } else {1363                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1364                }1365            },1366            CollectionMode::Fungible(_) => {1367                if let CreateItemData::Fungible(_) = data {1368                } else {1369                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1370                }1371            },1372            CollectionMode::ReFungible(_) => {1373                if let CreateItemData::ReFungible(data) = data {13741375                    // check sizes1376                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1377                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1378                } else {1379                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1380                }1381            },1382            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1383        };13841385        Ok(())1386    }13871388    fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1389        match data1390        {1391            CreateItemData::NFT(data) => {1392                let item = NftItemType {1393                    collection: collection_id,1394                    owner,1395                    const_data: data.const_data,1396                    variable_data: data.variable_data1397                };13981399                Self::add_nft_item(item)?;1400            },1401            CreateItemData::Fungible(_) => {1402                let item = FungibleItemType {1403                    collection: collection_id,1404                    owner,1405                    value: (10 as u128).pow(collection.decimal_points)1406                };14071408                Self::add_fungible_item(item)?;1409            },1410            CreateItemData::ReFungible(data) => {1411                let mut owner_list = Vec::new();1412                let value = (10 as u128).pow(collection.decimal_points);1413                owner_list.push(Ownership {owner: owner.clone(), fraction: value});14141415                let item = ReFungibleItemType {1416                    collection: collection_id,1417                    owner: owner_list,1418                    const_data: data.const_data,1419                    variable_data: data.variable_data1420                };14211422                Self::add_refungible_item(item)?;1423            }1424        };142514261427        // call event1428        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));14291430        Ok(())1431    }14321433    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1434        let current_index = <ItemListIndex>::get(item.collection)1435            .checked_add(1)1436            .ok_or(Error::<T>::NumOverflow)?;1437        let itemcopy = item.clone();1438        let owner = item.owner.clone();1439        let value = item.value as u64;14401441        Self::add_token_index(item.collection, current_index, owner.clone())?;14421443        <ItemListIndex>::insert(item.collection, current_index);1444        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14451446        // Add current block1447        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1448        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1449        1450        // Update balance1451        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1452            .checked_add(value)1453            .ok_or(Error::<T>::NumOverflow)?;1454        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14551456        Ok(())1457    }14581459    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1460        let current_index = <ItemListIndex>::get(item.collection)1461            .checked_add(1)1462            .ok_or(Error::<T>::NumOverflow)?;1463        let itemcopy = item.clone();14641465        let value = item.owner.first().unwrap().fraction as u64;1466        let owner = item.owner.first().unwrap().owner.clone();14671468        Self::add_token_index(item.collection, current_index, owner.clone())?;14691470        <ItemListIndex>::insert(item.collection, current_index);1471        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14721473        // Add current block1474        let block_number: T::BlockNumber = 0.into();1475        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);14761477        // Update balance1478        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1479            .checked_add(value)1480            .ok_or(Error::<T>::NumOverflow)?;1481        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14821483        Ok(())1484    }14851486    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1487        let current_index = <ItemListIndex>::get(item.collection)1488            .checked_add(1)1489            .ok_or(Error::<T>::NumOverflow)?;14901491        let item_owner = item.owner.clone();1492        let collection_id = item.collection.clone();1493        Self::add_token_index(collection_id, current_index, item.owner.clone())?;14941495        <ItemListIndex>::insert(collection_id, current_index);1496        <NftItemList<T>>::insert(collection_id, current_index, item);14971498        // Add current block1499        let block_number: T::BlockNumber = 0.into();1500        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);15011502        // Update balance1503        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1504            .checked_add(1)1505            .ok_or(Error::<T>::NumOverflow)?;1506        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);15071508        Ok(())1509    }15101511    fn burn_refungible_item(1512        collection_id: u64,1513        item_id: u64,1514        owner: T::AccountId,1515    ) -> DispatchResult {1516        ensure!(1517            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1518            Error::<T>::TokenNotFound1519        );1520        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1521        let item = collection1522            .owner1523            .iter()1524            .filter(|&i| i.owner == owner)1525            .next()1526            .unwrap();1527        Self::remove_token_index(collection_id, item_id, owner.clone())?;15281529        // remove approve list1530        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));15311532        // update balance1533        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1534            .checked_sub(item.fraction as u64)1535            .ok_or(Error::<T>::NumOverflow)?;1536        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15371538        <ReFungibleItemList<T>>::remove(collection_id, item_id);15391540        Ok(())1541    }15421543    fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1544        ensure!(1545            <NftItemList<T>>::contains_key(collection_id, item_id),1546            Error::<T>::TokenNotFound1547        );1548        let item = <NftItemList<T>>::get(collection_id, item_id);1549        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15501551        // remove approve list1552        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15531554        // update balance1555        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1556            .checked_sub(1)1557            .ok_or(Error::<T>::NumOverflow)?;1558        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1559        <NftItemList<T>>::remove(collection_id, item_id);15601561        Ok(())1562    }15631564    fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1565        ensure!(1566            <FungibleItemList<T>>::contains_key(collection_id, item_id),1567            Error::<T>::TokenNotFound1568        );1569        let item = <FungibleItemList<T>>::get(collection_id, item_id);1570        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15711572        // remove approve list1573        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15741575        // update balance1576        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1577            .checked_sub(item.value as u64)1578            .ok_or(Error::<T>::NumOverflow)?;1579        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15801581        <FungibleItemList<T>>::remove(collection_id, item_id);15821583        Ok(())1584    }15851586    fn collection_exists(collection_id: u64) -> DispatchResult {1587        ensure!(1588            <Collection<T>>::contains_key(collection_id),1589            Error::<T>::CollectionNotFound1590        );1591        Ok(())1592    }15931594    fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1595        Self::collection_exists(collection_id)?;15961597        let target_collection = <Collection<T>>::get(collection_id);1598        ensure!(1599            subject == target_collection.owner,1600            Error::<T>::NoPermission1601        );16021603        Ok(())1604    }16051606    fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1607        let target_collection = <Collection<T>>::get(collection_id);1608        let mut result: bool = subject == target_collection.owner;1609        let exists = <AdminList<T>>::contains_key(collection_id);16101611        if !result & exists {1612            if <AdminList<T>>::get(collection_id).contains(&subject) {1613                result = true1614            }1615        }16161617        result1618    }16191620    fn check_owner_or_admin_permissions(1621        collection_id: u64,1622        subject: T::AccountId,1623    ) -> DispatchResult {1624        Self::collection_exists(collection_id)?;1625        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());16261627        ensure!(1628            result,1629            Error::<T>::NoPermission1630        );1631        Ok(())1632    }16331634    fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1635        let target_collection = <Collection<T>>::get(collection_id);16361637        match target_collection.mode {1638            CollectionMode::NFT => {1639                <NftItemList<T>>::get(collection_id, item_id).owner == subject1640            }1641            CollectionMode::Fungible(_) => {1642                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1643            }1644            CollectionMode::ReFungible(_) => {1645                <ReFungibleItemList<T>>::get(collection_id, item_id)1646                    .owner1647                    .iter()1648                    .any(|i| i.owner == subject)1649            }1650            CollectionMode::Invalid => false,1651        }1652    }16531654    fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1655        let mes = Error::<T>::AddresNotInWhiteList;1656        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1657        let wl = <WhiteList<T>>::get(collection_id);1658        ensure!(wl.contains(address), mes);16591660        Ok(())1661    }16621663    fn transfer_fungible(1664        collection_id: u64,1665        item_id: u64,1666        value: u64,1667        owner: T::AccountId,1668        new_owner: T::AccountId,1669    ) -> DispatchResult {1670        ensure!(1671            <FungibleItemList<T>>::contains_key(collection_id, item_id),1672            Error::<T>::TokenNotFound1673        );16741675        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1676        let amount = full_item.value;16771678        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);16791680        // update balance1681        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1682            .checked_sub(value)1683            .ok_or(Error::<T>::NumOverflow)?;1684        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);16851686        let mut new_owner_account_id = 0;1687        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1688        if new_owner_items.len() > 0 {1689            new_owner_account_id = new_owner_items[0];1690        }16911692        let val64 = value.into();16931694        // transfer1695        if amount == val64 && new_owner_account_id == 0 {1696            // change owner1697            // new owner do not have account1698            let mut new_full_item = full_item.clone();1699            new_full_item.owner = new_owner.clone();1700            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17011702            // update balance1703            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1704                .checked_add(value)1705                .ok_or(Error::<T>::NumOverflow)?;1706            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17071708            // update index collection1709            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1710        } else {1711            let mut new_full_item = full_item.clone();1712            new_full_item.value -= val64;17131714            // separate amount1715            if new_owner_account_id > 0 {1716                // new owner has account1717                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1718                item.value += val64;17191720                // update balance1721                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1722                    .checked_add(value)1723                    .ok_or(Error::<T>::NumOverflow)?;1724                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17251726                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1727            } else {1728                // new owner do not have account1729                let item = FungibleItemType {1730                    collection: collection_id,1731                    owner: new_owner.clone(),1732                    value: val64,1733                };17341735                Self::add_fungible_item(item)?;1736            }17371738            if amount == val64 {1739                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;17401741                // remove approve list1742                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1743                <FungibleItemList<T>>::remove(collection_id, item_id);1744            }17451746            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1747        }17481749        Ok(())1750    }17511752    fn transfer_refungible(1753        collection_id: u64,1754        item_id: u64,1755        value: u64,1756        owner: T::AccountId,1757        new_owner: T::AccountId,1758    ) -> DispatchResult {1759        ensure!(1760            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1761            Error::<T>::TokenNotFound1762        );17631764        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1765        let item = full_item1766            .owner1767            .iter()1768            .filter(|i| i.owner == owner)1769            .next()1770            .ok_or(Error::<T>::NumOverflow)?;1771        let amount = item.fraction;17721773        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);17741775        // update balance1776        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1777            .checked_sub(value)1778            .ok_or(Error::<T>::NumOverflow)?;1779        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);17801781        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1782            .checked_add(value)1783            .ok_or(Error::<T>::NumOverflow)?;1784        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17851786        let old_owner = item.owner.clone();1787        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1788        let val64 = value.into();17891790        // transfer1791        if amount == val64 && !new_owner_has_account {1792            // change owner1793            // new owner do not have account1794            let mut new_full_item = full_item.clone();1795            new_full_item1796                .owner1797                .iter_mut()1798                .find(|i| i.owner == owner)1799                .unwrap()1800                .owner = new_owner.clone();1801            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18021803            // update index collection1804            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1805        } else {1806            let mut new_full_item = full_item.clone();1807            new_full_item1808                .owner1809                .iter_mut()1810                .find(|i| i.owner == owner)1811                .unwrap()1812                .fraction -= val64;18131814            // separate amount1815            if new_owner_has_account {1816                // new owner has account1817                new_full_item1818                    .owner1819                    .iter_mut()1820                    .find(|i| i.owner == new_owner)1821                    .unwrap()1822                    .fraction += val64;1823            } else {1824                // new owner do not have account1825                new_full_item.owner.push(Ownership {1826                    owner: new_owner.clone(),1827                    fraction: val64,1828                });1829                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1830            }18311832            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1833        }18341835        Ok(())1836    }18371838    fn transfer_nft(1839        collection_id: u64,1840        item_id: u64,1841        sender: T::AccountId,1842        new_owner: T::AccountId,1843    ) -> DispatchResult {1844        ensure!(1845            <NftItemList<T>>::contains_key(collection_id, item_id),1846            Error::<T>::TokenNotFound1847        );18481849        let mut item = <NftItemList<T>>::get(collection_id, item_id);18501851        ensure!(1852            sender == item.owner,1853            Error::<T>::MustBeTokenOwner1854        );18551856        // update balance1857        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1858            .checked_sub(1)1859            .ok_or(Error::<T>::NumOverflow)?;1860        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18611862        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1863            .checked_add(1)1864            .ok_or(Error::<T>::NumOverflow)?;1865        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18661867        // change owner1868        let old_owner = item.owner.clone();1869        item.owner = new_owner.clone();1870        <NftItemList<T>>::insert(collection_id, item_id, item);18711872        // update index collection1873        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;18741875        // reset approved list1876        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1877        Ok(())1878    }1879    1880    fn item_exists(1881        collection_id: u64,1882        item_id: u64,1883        mode: &CollectionMode1884    ) -> DispatchResult {1885        match mode {1886            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1887            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1888            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1889            _ => ()1890        };1891        1892        Ok(())1893    }18941895    fn set_re_fungible_variable_data(1896        collection_id: u64,1897        item_id: u64,1898        data: Vec<u8>1899    ) -> DispatchResult {1900        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);19011902        item.variable_data = data;19031904        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);19051906        Ok(())1907    }19081909    fn set_nft_variable_data(1910        collection_id: u64,1911        item_id: u64,1912        data: Vec<u8>1913    ) -> DispatchResult {1914        let mut item = <NftItemList<T>>::get(collection_id, item_id);1915        1916        item.variable_data = data;19171918        <NftItemList<T>>::insert(collection_id, item_id, item);1919        1920        Ok(())1921    }19221923    fn init_collection(item: &CollectionType<T::AccountId>) {1924        // check params1925        assert!(1926            item.decimal_points <= 4,1927            "decimal_points parameter must be lower than 4"1928        );1929        assert!(1930            item.name.len() <= 64,1931            "Collection name can not be longer than 63 char"1932        );1933        assert!(1934            item.name.len() <= 256,1935            "Collection description can not be longer than 255 char"1936        );1937        assert!(1938            item.token_prefix.len() <= 16,1939            "Token prefix can not be longer than 15 char"1940        );19411942        // Generate next collection ID1943        let next_id = CreatedCollectionCount::get()1944            .checked_add(1)1945            .unwrap();19461947        CreatedCollectionCount::put(next_id);1948    }19491950    fn init_nft_token(item: &NftItemType<T::AccountId>) {1951        let current_index = <ItemListIndex>::get(item.collection)1952            .checked_add(1)1953            .unwrap();19541955        let item_owner = item.owner.clone();1956        let collection_id = item.collection.clone();1957        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();19581959        <ItemListIndex>::insert(collection_id, current_index);19601961        // Update balance1962        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1963            .checked_add(1)1964            .unwrap();1965        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1966    }19671968    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1969        let current_index = <ItemListIndex>::get(item.collection)1970            .checked_add(1)1971            .unwrap();1972        let owner = item.owner.clone();1973        let value = item.value as u64;19741975        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();19761977        <ItemListIndex>::insert(item.collection, current_index);19781979        // Update balance1980        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1981            .checked_add(value)1982            .unwrap();1983        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1984    }19851986    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1987        let current_index = <ItemListIndex>::get(item.collection)1988            .checked_add(1)1989            .unwrap();19901991        let value = item.owner.first().unwrap().fraction as u64;1992        let owner = item.owner.first().unwrap().owner.clone();19931994        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();19951996        <ItemListIndex>::insert(item.collection, current_index);19971998        // Update balance1999        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2000            .checked_add(value)2001            .unwrap();2002        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2003    }20042005    fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {20062007        // add to account limit2008        if <AccountItemCount<T>>::contains_key(owner.clone()) {20092010            // bound Owned tokens by a single address2011            let count = <AccountItemCount<T>>::get(owner.clone());2012            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);20132014            <AccountItemCount<T>>::insert(owner.clone(), count2015                .checked_add(1)2016                .ok_or(Error::<T>::NumOverflow)?);2017        }2018        else {2019            <AccountItemCount<T>>::insert(owner.clone(), 1);2020        }20212022        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2023        if list_exists {2024            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2025            let item_contains = list.contains(&item_index.clone());20262027            if !item_contains {2028                list.push(item_index.clone());2029            }20302031            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2032        } else {2033            let mut itm = Vec::new();2034            itm.push(item_index.clone());2035            <AddressTokens<T>>::insert(collection_id, owner, itm);2036            2037        }20382039        Ok(())2040    }20412042    fn remove_token_index(2043        collection_id: u64,2044        item_index: u64,2045        owner: T::AccountId,2046    ) -> DispatchResult {20472048        // update counter2049        <AccountItemCount<T>>::insert(owner.clone(), 2050            <AccountItemCount<T>>::get(owner.clone())2051            .checked_sub(1)2052            .ok_or(Error::<T>::NumOverflow)?);205320542055        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2056        if list_exists {2057            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2058            let item_contains = list.contains(&item_index.clone());20592060            if item_contains {2061                list.retain(|&item| item != item_index);2062                <AddressTokens<T>>::insert(collection_id, owner, list);2063            }2064        }20652066        Ok(())2067    }20682069    fn move_token_index(2070        collection_id: u64,2071        item_index: u64,2072        old_owner: T::AccountId,2073        new_owner: T::AccountId,2074    ) -> DispatchResult {2075        Self::remove_token_index(collection_id, item_index, old_owner)?;2076        Self::add_token_index(collection_id, item_index, new_owner)?;20772078        Ok(())2079    }2080}20812082////////////////////////////////////////////////////////////////////////////////////////////////////2083// Economic models2084// #region20852086/// Fee multiplier.2087pub type Multiplier = FixedU128;20882089type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2090    <T as system::Trait>::AccountId,2091>>::Balance;2092type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2093    <T as system::Trait>::AccountId,2094>>::NegativeImbalance;20952096/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2097/// in the queue.2098#[derive(Encode, Decode, Clone, Eq, PartialEq)]2099pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2100    #[codec(compact)] BalanceOf<T>2101);21022103impl<T: Trait + Send + Sync> sp_std::fmt::Debug2104    for ChargeTransactionPayment<T>2105{2106    #[cfg(feature = "std")]2107    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2108        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2109    }2110    #[cfg(not(feature = "std"))]2111    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2112        Ok(())2113    }2114}21152116impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2117where2118    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2119    BalanceOf<T>: Send + Sync + FixedPointOperand,2120{2121    /// utility constructor. Used only in client/factory code.2122    pub fn from(fee: BalanceOf<T>) -> Self {2123        Self(fee)2124    }21252126    pub fn traditional_fee(2127        len: usize,2128        info: &DispatchInfoOf<T::Call>,2129        tip: BalanceOf<T>,2130    ) -> BalanceOf<T>2131    where2132        T::Call: Dispatchable<Info = DispatchInfo>,2133    {2134        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2135    }21362137    fn withdraw_fee(2138        &self,2139        who: &T::AccountId,2140        call: &T::Call,2141        info: &DispatchInfoOf<T::Call>,2142        len: usize,2143    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2144        let tip = self.0;21452146        // Set fee based on call type. Creating collection costs 1 Unique.2147        // All other transactions have traditional fees so far2148        // let fee = match call.is_sub_type() {2149        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2150        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2151        //                                                 // _ => <BalanceOf<T>>::from(100)2152        // };2153        let fee = Self::traditional_fee(len, info, tip);21542155        // Determine who is paying transaction fee based on ecnomic model2156        // Parse call to extract collection ID and access collection sponsor2157        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2158            Some(Call::create_item(collection_id, _properties, _owner)) => {2159                <Collection<T>>::get(collection_id).sponsor2160            }2161            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2162                let _collection_mode = <Collection<T>>::get(collection_id).mode;21632164                // sponsor timeout2165                let sponsor_transfer = match _collection_mode {2166                    CollectionMode::NFT => {2167                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2168                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2169                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2170                        if block_number >= limit_time {2171                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2172                            true2173                        }2174                        else {2175                            false2176                        }2177                    }2178                    CollectionMode::Fungible(_) => {2179                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2180                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2181                        if basket.iter().any(|i| i.address == _new_owner.clone())2182                        {2183                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2184                            let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2185                            if block_number >= limit_time {2186                                basket.retain(|x| x.address == item.address);2187                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2188                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2189                                true2190                            }2191                            else {2192                                false2193                            }2194                        }2195                        else {2196                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2197                            true2198                        }2199                    }2200                    CollectionMode::ReFungible(_) => {2201                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2202                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2203                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2204                        if block_number >= limit_time {2205                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2206                            true2207                        } else {2208                            false2209                        }2210                    }2211                    _ => {2212                        false2213                    },2214                };22152216                if !sponsor_transfer {2217                    T::AccountId::default()2218                } else {2219                    <Collection<T>>::get(collection_id).sponsor2220                }2221            }22222223            _ => T::AccountId::default(),2224        };22252226        // Sponsor smart contracts2227        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {22282229            // On instantiation: set the contract owner2230            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {22312232                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2233                    code_hash,2234                    &data,2235                    &who,2236                );2237                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());22382239                T::AccountId::default()2240            },22412242            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2243            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {22442245                let mut sp = T::AccountId::default();2246                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());2247                if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2248                    if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2249                        sp = called_contract;2250                    }2251                }22522253                sp2254            },22552256            _ => sponsor,2257        };22582259        let mut who_pays_fee: T::AccountId = sponsor.clone();2260        if sponsor == T::AccountId::default() {2261            who_pays_fee = who.clone();2262        }22632264        // Only mess with balances if fee is not zero.2265        if fee.is_zero() {2266            return Ok((fee, None));2267        }22682269        match <T as transaction_payment::Trait>::Currency::withdraw(2270            &who_pays_fee,2271            fee,2272            if tip.is_zero() {2273                WithdrawReason::TransactionPayment.into()2274            } else {2275                WithdrawReason::TransactionPayment | WithdrawReason::Tip2276            },2277            ExistenceRequirement::KeepAlive,2278        ) {2279            Ok(imbalance) => Ok((fee, Some(imbalance))),2280            Err(_) => Err(InvalidTransaction::Payment.into()),2281        }2282    }2283}228422852286impl<T: Trait + Send + Sync> SignedExtension2287    for ChargeTransactionPayment<T>2288where2289    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2290    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2291{2292    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2293    type AccountId = T::AccountId;2294    type Call = T::Call;2295    type AdditionalSigned = ();2296    type Pre = (2297        BalanceOf<T>,2298        Self::AccountId,2299        Option<NegativeImbalanceOf<T>>,2300        BalanceOf<T>,2301    );2302    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2303        Ok(())2304    }23052306    fn validate(2307        &self,2308        _who: &Self::AccountId,2309        _call: &Self::Call,2310        _info: &DispatchInfoOf<Self::Call>,2311        _len: usize,2312    ) -> TransactionValidity {2313        Ok(ValidTransaction::default())2314    }23152316    fn pre_dispatch(2317        self,2318        who: &Self::AccountId,2319        call: &Self::Call,2320        info: &DispatchInfoOf<Self::Call>,2321        len: usize,2322    ) -> Result<Self::Pre, TransactionValidityError> {2323        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2324        Ok((self.0, who.clone(), imbalance, fee))2325    }23262327    fn post_dispatch(2328        pre: Self::Pre,2329        info: &DispatchInfoOf<Self::Call>,2330        post_info: &PostDispatchInfoOf<Self::Call>,2331        len: usize,2332        _result: &DispatchResult,2333    ) -> Result<(), TransactionValidityError> {2334        let (tip, who, imbalance, fee) = pre;2335        if let Some(payed) = imbalance {2336            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2337                len as u32, info, post_info, tip,2338            );2339            let refund = fee.saturating_sub(actual_fee);2340            let actual_payment =2341                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2342                    &who, refund,2343                ) {2344                    Ok(refund_imbalance) => {2345                        // The refund cannot be larger than the up front payed max weight.2346                        // `PostDispatchInfo::calc_unspent` guards against such a case.2347                        match payed.offset(refund_imbalance) {2348                            Ok(actual_payment) => actual_payment,2349                            Err(_) => return Err(InvalidTransaction::Payment.into()),2350                        }2351                    }2352                    // We do not recreate the account using the refund. The up front payment2353                    // is gone in that case.2354                    Err(_) => payed,2355                };2356            let imbalances = actual_payment.split(tip);2357            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2358                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2359            );2360        }2361        Ok(())2362    }2363}23642365// #endregion
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 exceeded.268        TotalCollectionsLimitExceeded,269		/// Decimal_points parameter must be lower than 4.270        CollectionDecimalPointLimitExceeded, 271        /// Collection name can not be longer than 63 char.272        CollectionNameLimitExceeded, 273        /// Collection description can not be longer than 255 char.274        CollectionDescriptionLimitExceeded, 275        /// Token prefix can not be longer than 15 char.276        CollectionTokenPrefixLimitExceeded,277        /// This collection does not exist.278        CollectionNotFound,279        /// Item not exists.280        TokenNotFound,281        /// Arithmetic calculation overflow.282        NumOverflow,       283        /// Account already has admin role.284        AlreadyAdmin,  285        /// You do not own this collection.286        NoPermission,287        /// This address is not set as sponsor, use setCollectionSponsor first.288        ConfirmUnsetSponsorFail,289        /// Collection is not in mint mode.290        PublicMintingNotAllowed,291        /// Sender parameter and item owner must be equal.292        MustBeTokenOwner,293        /// Item balance not enough.294        TokenValueTooLow,295        /// Size of item is too large.296        NftSizeLimitExceeded,297        /// No approve found298        ApproveNotFound,299        /// Requested value more than approved.300        TokenValueNotEnough,301        /// Only approved addresses can call this method.302        ApproveRequired,303        /// Address is not in white list.304        AddresNotInWhiteList,305        /// Number of collection admins bound exceeded.306        CollectionAdminsLimitExceeded,307        /// Owned tokens by a single address bound exceeded.308        AddressOwnershipLimitExceeded,309        /// Length of items properties must be greater than 0.310        EmptyArgument,311        /// const_data exceeded data limit.312        TokenConstDataLimitExceeded,313        /// variable_data exceeded data limit.314        TokenVariableDataLimitExceeded,315        /// Not NFT item data used to mint in NFT collection.316        NotNftDataUsedToMintNftCollectionToken,317        /// Not Fungible item data used to mint in Fungible collection.318        NotFungibleDataUsedToMintFungibleCollectionToken,319        /// Not Re Fungible item data used to mint in Re Fungible collection.320        NotReFungibleDataUsedToMintReFungibleCollectionToken,321        /// Unexpected collection type.322        UnexpectedCollectionType,323        /// Can't store metadata in fungible tokens.324        CantSotreMetadataInFungibleTokens325	}326}327328pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {329    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;330331    /// Weight information for extrinsics in this pallet.332	type WeightInfo: WeightInfo;333}334335#[cfg(feature = "runtime-benchmarks")]336mod benchmarking;337338// #endregion339340decl_storage! {341    trait Store for Module<T: Trait> as Nft {342343        // Private members344        NextCollectionID: u64;345        CreatedCollectionCount: u64;346        ChainVersion: u64;347        ItemListIndex: map hasher(blake2_128_concat) u64 => u64;348349        // Chain limits struct350        pub ChainLimit get(fn chain_limit) config(): ChainLimits;351352        // Bound counters353        CollectionCount: u64;354        pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;355356        // Basic collections357        pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;358        pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;359        pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;360361        /// Balance owner per collection map362        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;363364        /// second parameter: item id + owner account id365        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;366367        /// Item collections368        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;369        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;370        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;371372        /// Index list373        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;374375        /// Tokens transfer baskets376        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;377        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>>;378        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;379380        // Contract Sponsorship and Ownership381        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;382        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;383        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;384        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;385    }386    add_extra_genesis {387        build(|config: &GenesisConfig<T>| {388            // Modification of storage389            for (_num, _c) in &config.collection {390                <Module<T>>::init_collection(_c);391            }392393            for (_num, _q, _i) in &config.nft_item_id {394                <Module<T>>::init_nft_token(_i);395            }396397            for (_num, _q, _i) in &config.fungible_item_id {398                <Module<T>>::init_fungible_token(_i);399            }400401            for (_num, _q, _i) in &config.refungible_item_id {402                <Module<T>>::init_refungible_token(_i);403            }404        })405    }406}407408decl_event!(409    pub enum Event<T>410    where411        AccountId = <T as system::Trait>::AccountId,412    {413        /// New collection was created414        /// 415        /// # Arguments416        /// 417        /// * collection_id: Globally unique identifier of newly created collection.418        /// 419        /// * mode: [CollectionMode] converted into u8.420        /// 421        /// * account_id: Collection owner.422        Created(u64, u8, AccountId),423424        /// New item was created.425        /// 426        /// # Arguments427        /// 428        /// * collection_id: Id of the collection where item was created.429        /// 430        /// * item_id: Id of an item. Unique within the collection.431        ItemCreated(u64, u64),432433        /// Collection item was burned.434        /// 435        /// # Arguments436        /// 437        /// collection_id.438        /// 439        /// item_id: Identifier of burned NFT.440        ItemDestroyed(u64, u64),441    }442);443444decl_module! {445    pub struct Module<T: Trait> for enum Call where origin: T::Origin {446447        fn deposit_event() = default;448        type Error = Error<T>;449450        fn on_initialize(now: T::BlockNumber) -> Weight {451452            if ChainVersion::get() < 2453            {454                let value = NextCollectionID::get();455                CreatedCollectionCount::put(value);456                ChainVersion::put(2);457            }458459            0460        }461462        /// 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.463        /// 464        /// # Permissions465        /// 466        /// * Anyone.467        /// 468        /// # Arguments469        /// 470        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.471        /// 472        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.473        /// 474        /// * token_prefix: UTF-8 string with token prefix.475        /// 476        /// * mode: [CollectionMode] collection type and type dependent data.477        // returns collection ID478        #[weight = T::WeightInfo::create_collection()]479        pub fn create_collection(origin,480                                 collection_name: Vec<u16>,481                                 collection_description: Vec<u16>,482                                 token_prefix: Vec<u8>,483                                 mode: CollectionMode) -> DispatchResult {484485            // Anyone can create a collection486            let who = ensure_signed(origin)?;487488            let decimal_points = match mode {489                CollectionMode::Fungible(points) => points,490                CollectionMode::ReFungible(points) => points,491                _ => 0492            };493494            // bound Total number of collections495            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);496497            // check params498            ensure!(decimal_points <= 4, Error::<T>::CollectionDecimalPointLimitExceeded);499500            let mut name = collection_name.to_vec();501            name.push(0);502            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);503504            let mut description = collection_description.to_vec();505            description.push(0);506            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);507508            let mut prefix = token_prefix.to_vec();509            prefix.push(0);510            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);511512            // Generate next collection ID513            let next_id = CreatedCollectionCount::get()514                .checked_add(1)515                .ok_or(Error::<T>::NumOverflow)?;516517            // bound counter518            let total = CollectionCount::get()519                .checked_add(1)520                .ok_or(Error::<T>::NumOverflow)?;521522            CreatedCollectionCount::put(next_id);523            CollectionCount::put(total);524525            // Create new collection526            let new_collection = CollectionType {527                owner: who.clone(),528                name: name,529                mode: mode.clone(),530                mint_mode: false,531                access: AccessMode::Normal,532                description: description,533                decimal_points: decimal_points,534                token_prefix: prefix,535                offchain_schema: Vec::new(),536                sponsor: T::AccountId::default(),537                unconfirmed_sponsor: T::AccountId::default(),538                variable_on_chain_schema: Vec::new(),539                const_on_chain_schema: Vec::new(),540            };541542            // Add new collection to map543            <Collection<T>>::insert(next_id, new_collection);544545            // call event546            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));547548            Ok(())549        }550551        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.552        /// 553        /// # Permissions554        /// 555        /// * Collection Owner.556        /// 557        /// # Arguments558        /// 559        /// * collection_id: collection to destroy.560        #[weight = T::WeightInfo::destroy_collection()]561        pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {562563            let sender = ensure_signed(origin)?;564            Self::check_owner_permissions(collection_id, sender)?;565566            <AddressTokens<T>>::remove_prefix(collection_id);567            <ApprovedList<T>>::remove_prefix(collection_id);568            <Balance<T>>::remove_prefix(collection_id);569            <ItemListIndex>::remove(collection_id);570            <AdminList<T>>::remove(collection_id);571            <Collection<T>>::remove(collection_id);572            <WhiteList<T>>::remove(collection_id);573574            <NftItemList<T>>::remove_prefix(collection_id);575            <FungibleItemList<T>>::remove_prefix(collection_id);576            <ReFungibleItemList<T>>::remove_prefix(collection_id);577578            <NftTransferBasket<T>>::remove_prefix(collection_id);579            <FungibleTransferBasket<T>>::remove_prefix(collection_id);580            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);581582            if CollectionCount::get() > 0583            {584                // bound couter585                let total = CollectionCount::get()586                    .checked_sub(1)587                    .ok_or(Error::<T>::NumOverflow)?;588589                CollectionCount::put(total);590            }591592            Ok(())593        }594595        /// Add an address to white list.596        /// 597        /// # Permissions598        /// 599        /// * Collection Owner600        /// * Collection Admin601        /// 602        /// # Arguments603        /// 604        /// * collection_id.605        /// 606        /// * address.607        #[weight = T::WeightInfo::add_to_white_list()]608        pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{609610            let sender = ensure_signed(origin)?;611            Self::check_owner_or_admin_permissions(collection_id, sender)?;612613            let mut white_list_collection: Vec<T::AccountId>;614            if <WhiteList<T>>::contains_key(collection_id) {615                white_list_collection = <WhiteList<T>>::get(collection_id);616                if !white_list_collection.contains(&address.clone())617                {618                    white_list_collection.push(address.clone());619                }620            }621            else {622                white_list_collection = Vec::new();623                white_list_collection.push(address.clone());624            }625626            <WhiteList<T>>::insert(collection_id, white_list_collection);627            Ok(())628        }629630        /// Remove an address from white list.631        /// 632        /// # Permissions633        /// 634        /// * Collection Owner635        /// * Collection Admin636        /// 637        /// # Arguments638        /// 639        /// * collection_id.640        /// 641        /// * address.642        #[weight = T::WeightInfo::remove_from_white_list()]643        pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{644645            let sender = ensure_signed(origin)?;646            Self::check_owner_or_admin_permissions(collection_id, sender)?;647648            if <WhiteList<T>>::contains_key(collection_id) {649                let mut white_list_collection = <WhiteList<T>>::get(collection_id);650                if white_list_collection.contains(&address.clone())651                {652                    white_list_collection.retain(|i| *i != address.clone());653                    <WhiteList<T>>::insert(collection_id, white_list_collection);654                }655            }656657            Ok(())658        }659660        /// Toggle between normal and white list access for the methods with access for `Anyone`.661        /// 662        /// # Permissions663        /// 664        /// * Collection Owner.665        /// 666        /// # Arguments667        /// 668        /// * collection_id.669        /// 670        /// * mode: [AccessMode]671        #[weight = T::WeightInfo::set_public_access_mode()]672        pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult673        {674            let sender = ensure_signed(origin)?;675676            Self::check_owner_permissions(collection_id, sender)?;677            let mut target_collection = <Collection<T>>::get(collection_id);678            target_collection.access = mode;679            <Collection<T>>::insert(collection_id, target_collection);680681            Ok(())682        }683684        /// Allows Anyone to create tokens if:685        /// * White List is enabled, and686        /// * Address is added to white list, and687        /// * This method was called with True parameter688        /// 689        /// # Permissions690        /// * Collection Owner691        ///692        /// # Arguments693        /// 694        /// * collection_id.695        /// 696        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.697        #[weight = T::WeightInfo::set_mint_permission()]698        pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult699        {700            let sender = ensure_signed(origin)?;701702            Self::check_owner_permissions(collection_id, sender)?;703            let mut target_collection = <Collection<T>>::get(collection_id);704            target_collection.mint_mode = mint_permission;705            <Collection<T>>::insert(collection_id, target_collection);706707            Ok(())708        }709710        /// Change the owner of the collection.711        /// 712        /// # Permissions713        /// 714        /// * Collection Owner.715        /// 716        /// # Arguments717        /// 718        /// * collection_id.719        /// 720        /// * new_owner.721        #[weight = T::WeightInfo::change_collection_owner()]722        pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {723724            let sender = ensure_signed(origin)?;725            Self::check_owner_permissions(collection_id, sender)?;726            let mut target_collection = <Collection<T>>::get(collection_id);727            target_collection.owner = new_owner;728            <Collection<T>>::insert(collection_id, target_collection);729730            Ok(())731        }732733        /// Adds an admin of the Collection.734        /// 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. 735        /// 736        /// # Permissions737        /// 738        /// * Collection Owner.739        /// * Collection Admin.740        /// 741        /// # Arguments742        /// 743        /// * collection_id: ID of the Collection to add admin for.744        /// 745        /// * new_admin_id: Address of new admin to add.746        #[weight = T::WeightInfo::add_collection_admin()]747        pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {748749            let sender = ensure_signed(origin)?;750            Self::check_owner_or_admin_permissions(collection_id, sender)?;751            let mut admin_arr: Vec<T::AccountId> = Vec::new();752753            if <AdminList<T>>::contains_key(collection_id)754            {755                admin_arr = <AdminList<T>>::get(collection_id);756                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);757            }758759            // Number of collection admins760            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);761762            admin_arr.push(new_admin_id);763            <AdminList<T>>::insert(collection_id, admin_arr);764765            Ok(())766        }767768        /// 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.769        ///770        /// # Permissions771        /// 772        /// * Collection Owner.773        /// * Collection Admin.774        /// 775        /// # Arguments776        /// 777        /// * collection_id: ID of the Collection to remove admin for.778        /// 779        /// * account_id: Address of admin to remove.780        #[weight = T::WeightInfo::remove_collection_admin()]781        pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {782783            let sender = ensure_signed(origin)?;784            Self::check_owner_or_admin_permissions(collection_id, sender)?;785786            if <AdminList<T>>::contains_key(collection_id)787            {788                let mut admin_arr = <AdminList<T>>::get(collection_id);789                admin_arr.retain(|i| *i != account_id);790                <AdminList<T>>::insert(collection_id, admin_arr);791            }792793            Ok(())794        }795796        /// # Permissions797        /// 798        /// * Collection Owner799        /// 800        /// # Arguments801        /// 802        /// * collection_id.803        /// 804        /// * new_sponsor.805        #[weight = T::WeightInfo::set_collection_sponsor()]806        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {807808            let sender = ensure_signed(origin)?;809            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);810811            let mut target_collection = <Collection<T>>::get(collection_id);812            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);813814            target_collection.unconfirmed_sponsor = new_sponsor;815            <Collection<T>>::insert(collection_id, target_collection);816817            Ok(())818        }819820        /// # Permissions821        /// 822        /// * Sponsor.823        /// 824        /// # Arguments825        /// 826        /// * collection_id.827        #[weight = T::WeightInfo::confirm_sponsorship()]828        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {829830            let sender = ensure_signed(origin)?;831            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);832833            let mut target_collection = <Collection<T>>::get(collection_id);834            ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);835836            target_collection.sponsor = target_collection.unconfirmed_sponsor;837            target_collection.unconfirmed_sponsor = T::AccountId::default();838            <Collection<T>>::insert(collection_id, target_collection);839840            Ok(())841        }842843        /// Switch back to pay-per-own-transaction model.844        ///845        /// # Permissions846        ///847        /// * Collection owner.848        /// 849        /// # Arguments850        /// 851        /// * collection_id.852        #[weight = T::WeightInfo::remove_collection_sponsor()]853        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {854855            let sender = ensure_signed(origin)?;856            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);857858            let mut target_collection = <Collection<T>>::get(collection_id);859            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);860861            target_collection.sponsor = T::AccountId::default();862            <Collection<T>>::insert(collection_id, target_collection);863864            Ok(())865        }866867        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.868        /// 869        /// # Permissions870        /// 871        /// * Collection Owner.872        /// * Collection Admin.873        /// * Anyone if874        ///     * White List is enabled, and875        ///     * Address is added to white list, and876        ///     * MintPermission is enabled (see SetMintPermission method)877        /// 878        /// # Arguments879        /// 880        /// * collection_id: ID of the collection.881        /// 882        /// * owner: Address, initial owner of the NFT.883        ///884        /// * data: Token data to store on chain.885        // #[weight =886        // (130_000_000 as Weight)887        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))888        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))889        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]890891        #[weight = T::WeightInfo::create_item(data.len())]892        pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {893894            let sender = ensure_signed(origin)?;895896            Self::collection_exists(collection_id)?;897898            let target_collection = <Collection<T>>::get(collection_id);899900            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;901            Self::validate_create_item_args(&target_collection, &data)?;902            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;903904            Ok(())905        }906907        /// This method creates multiple instances of NFT Collection created with CreateCollection method.908        /// 909        /// # Permissions910        /// 911        /// * Collection Owner.912        /// * Collection Admin.913        /// * Anyone if914        ///     * White List is enabled, and915        ///     * Address is added to white list, and916        ///     * MintPermission is enabled (see SetMintPermission method)917        /// 918        /// # Arguments919        /// 920        /// * collection_id: ID of the collection.921        /// 922        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].923        /// 924        /// * owner: Address, initial owner of the NFT.925        #[weight = T::WeightInfo::create_item(items_data.into_iter()926                               .map(|data| { data.len() })927                               .sum())]928        pub fn create_multiple_items(origin, collection_id: u64, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {929930            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);931            let sender = ensure_signed(origin)?;932933            Self::collection_exists(collection_id)?;934            let target_collection = <Collection<T>>::get(collection_id);935936            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;937938            for data in &items_data {939                Self::validate_create_item_args(&target_collection, data)?;940            }941            for data in &items_data {942                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;943            }944945            Ok(())946        }947948        /// Destroys a concrete instance of NFT.949        /// 950        /// # Permissions951        /// 952        /// * Collection Owner.953        /// * Collection Admin.954        /// * Current NFT Owner.955        /// 956        /// # Arguments957        /// 958        /// * collection_id: ID of the collection.959        /// 960        /// * item_id: ID of NFT to burn.961        #[weight = T::WeightInfo::burn_item()]962        pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {963964            let sender = ensure_signed(origin)?;965            Self::collection_exists(collection_id)?;966967            // Transfer permissions check968            let target_collection = <Collection<T>>::get(collection_id);969            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||970                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),971                Error::<T>::NoPermission);972973            if target_collection.access == AccessMode::WhiteList {974                Self::check_white_list(collection_id, &sender)?;975            }976977            match target_collection.mode978            {979                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,980                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,981                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,982                _ => ()983            };984985            // call event986            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));987988            Ok(())989        }990991        /// Change ownership of the token.992        /// 993        /// # Permissions994        /// 995        /// * Collection Owner996        /// * Collection Admin997        /// * Current NFT owner998        ///999        /// # Arguments1000        /// 1001        /// * recipient: Address of token recipient.1002        /// 1003        /// * collection_id.1004        /// 1005        /// * item_id: ID of the item1006        ///     * Non-Fungible Mode: Required.1007        ///     * Fungible Mode: Ignored.1008        ///     * Re-Fungible Mode: Required.1009        /// 1010        /// * value: Amount to transfer.1011        ///     * Non-Fungible Mode: Ignored1012        ///     * Fungible Mode: Must specify transferred amount1013        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1014        #[weight = T::WeightInfo::transfer()]1015        pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {10161017            let sender = ensure_signed(origin)?;10181019            // Transfer permissions check1020            let target_collection = <Collection<T>>::get(collection_id);1021            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1022                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1023                Error::<T>::NoPermission);10241025            if target_collection.access == AccessMode::WhiteList {1026                Self::check_white_list(collection_id, &sender)?;1027                Self::check_white_list(collection_id, &recipient)?;1028            }10291030            match target_collection.mode1031            {1032                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1033                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1034                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1035                _ => ()1036            };10371038            Ok(())1039        }10401041        /// Set, change, or remove approved address to transfer the ownership of the NFT.1042        /// 1043        /// # Permissions1044        /// 1045        /// * Collection Owner1046        /// * Collection Admin1047        /// * Current NFT owner1048        /// 1049        /// # Arguments1050        /// 1051        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1052        /// 1053        /// * collection_id.1054        /// 1055        /// * item_id: ID of the item.1056        #[weight = T::WeightInfo::approve()]1057        pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {10581059            let sender = ensure_signed(origin)?;10601061            // Transfer permissions check1062            let target_collection = <Collection<T>>::get(collection_id);1063            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1064                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1065                Error::<T>::NoPermission);10661067            if target_collection.access == AccessMode::WhiteList {1068                Self::check_white_list(collection_id, &sender)?;1069                Self::check_white_list(collection_id, &approved)?;1070            }10711072            // amount param stub1073            let amount = 100000000;10741075            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1076            if list_exists {10771078                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1079                let item_contains = list.iter().any(|i| i.approved == approved);10801081                if !item_contains {1082                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1083                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1084                }1085            } else {10861087                let mut list = Vec::new();1088                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1089                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1090            }10911092            Ok(())1093        }1094        1095        /// 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.1096        /// 1097        /// # Permissions1098        /// * Collection Owner1099        /// * Collection Admin1100        /// * Current NFT owner1101        /// * Address approved by current NFT owner1102        /// 1103        /// # Arguments1104        /// 1105        /// * from: Address that owns token.1106        /// 1107        /// * recipient: Address of token recipient.1108        /// 1109        /// * collection_id.1110        /// 1111        /// * item_id: ID of the item.1112        /// 1113        /// * value: Amount to transfer.1114        #[weight = T::WeightInfo::transfer_from()]1115        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {11161117            let sender = ensure_signed(origin)?;1118            let mut appoved_transfer = false;11191120            // Check approve1121            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1122                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1123                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1124                if opt_item.is_some()1125                {1126                    appoved_transfer = true;1127                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1128                }1129            }11301131            // Transfer permissions check1132            let target_collection = <Collection<T>>::get(collection_id);1133                ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1134                Error::<T>::NoPermission);11351136            if target_collection.access == AccessMode::WhiteList {1137                Self::check_white_list(collection_id, &sender)?;1138                Self::check_white_list(collection_id, &recipient)?;1139            }11401141            // remove approve1142            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1143                .into_iter().filter(|i| i.approved != sender.clone()).collect();1144            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);114511461147            match target_collection.mode1148            {1149                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1150                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1151                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1152                _ => ()1153            };11541155            Ok(())1156        }11571158        ///1159        #[weight = 0]1160        pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11611162            // let no_perm_mes = "You do not have permissions to modify this collection";1163            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1164            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1165            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11661167            // // on_nft_received  call11681169            // Self::transfer(origin, collection_id, item_id, new_owner)?;11701171            Ok(())1172        }11731174        /// Set off-chain data schema.1175        /// 1176        /// # Permissions1177        /// 1178        /// * Collection Owner1179        /// * Collection Admin1180        /// 1181        /// # Arguments1182        /// 1183        /// * collection_id.1184        /// 1185        /// * schema: String representing the offchain data schema.1186        #[weight = T::WeightInfo::set_variable_meta_data()]1187        pub fn set_variable_meta_data (1188            origin,1189            collection_id: u64,1190            item_id: u64,1191            data: Vec<u8>1192        ) -> DispatchResult {1193            let sender = ensure_signed(origin)?;1194            1195            Self::collection_exists(collection_id)?;11961197            // Modify permissions check1198            let target_collection = <Collection<T>>::get(collection_id);1199            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1200                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1201                Error::<T>::NoPermission);12021203            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12041205            match target_collection.mode1206            {1207                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1208                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1209                CollectionMode::Fungible(_) => fail!(Error::<T>::CantSotreMetadataInFungibleTokens),1210                _ => fail!(Error::<T>::UnexpectedCollectionType)1211            };12121213            Ok(())1214        }1215        12161217        /// Set off-chain data schema.1218        /// 1219        /// # Permissions1220        /// 1221        /// * Collection Owner1222        /// * Collection Admin1223        /// 1224        /// # Arguments1225        /// 1226        /// * collection_id.1227        /// 1228        /// * schema: String representing the offchain data schema.1229        #[weight = T::WeightInfo::set_offchain_schema()]1230        pub fn set_offchain_schema(1231            origin,1232            collection_id: u64,1233            schema: Vec<u8>1234        ) -> DispatchResult {1235            let sender = ensure_signed(origin)?;1236            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12371238            let mut target_collection = <Collection<T>>::get(collection_id);1239            target_collection.offchain_schema = schema;1240            <Collection<T>>::insert(collection_id, target_collection);12411242            Ok(())1243        }12441245        /// Set const on-chain data schema.1246        /// 1247        /// # Permissions1248        /// 1249        /// * Collection Owner1250        /// * Collection Admin1251        /// 1252        /// # Arguments1253        /// 1254        /// * collection_id.1255        /// 1256        /// * schema: String representing the const on-chain data schema.1257        #[weight = T::WeightInfo::set_const_on_chain_schema()]1258        pub fn set_const_on_chain_schema (1259            origin,1260            collection_id: u64,1261            schema: Vec<u8>1262        ) -> DispatchResult {1263            let sender = ensure_signed(origin)?;1264            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12651266            let mut target_collection = <Collection<T>>::get(collection_id);1267            target_collection.const_on_chain_schema = schema;1268            <Collection<T>>::insert(collection_id, target_collection);12691270            Ok(())1271        }12721273        /// Set variable on-chain data schema.1274        /// 1275        /// # Permissions1276        /// 1277        /// * Collection Owner1278        /// * Collection Admin1279        /// 1280        /// # Arguments1281        /// 1282        /// * collection_id.1283        /// 1284        /// * schema: String representing the variable on-chain data schema.1285        #[weight = T::WeightInfo::set_const_on_chain_schema()]1286        pub fn set_variable_on_chain_schema (1287            origin,1288            collection_id: u64,1289            schema: Vec<u8>1290        ) -> DispatchResult {1291            let sender = ensure_signed(origin)?;1292            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12931294            let mut target_collection = <Collection<T>>::get(collection_id);1295            target_collection.variable_on_chain_schema = schema;1296            <Collection<T>>::insert(collection_id, target_collection);12971298            Ok(())1299        }13001301        // Sudo permissions function1302        #[weight = 0]1303        pub fn set_chain_limits(1304            origin,1305            limits: ChainLimits1306        ) -> DispatchResult {1307            ensure_root(origin)?;1308            <ChainLimit>::put(limits);1309            Ok(())1310        }13111312        /// Enable smart contract self-sponsoring.1313        /// 1314        /// # Permissions1315        /// 1316        /// * Contract Owner1317        /// 1318        /// # Arguments1319        /// 1320        /// * contract address1321        /// * enable flag1322        /// 1323        #[weight = 0]1324        pub fn enable_contract_sponsoring(1325            origin,1326            contract_address: T::AccountId,1327            enable: bool1328        ) -> DispatchResult {1329            let sender = ensure_signed(origin)?;1330            let mut is_owner = false;1331            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1332                let owner = <ContractOwner<T>>::get(&contract_address);1333                is_owner = sender == owner;1334            }1335            ensure!(is_owner, Error::<T>::NoPermission);13361337            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1338            Ok(())1339        }13401341        /// Set the rate limit for contract sponsoring to specified number of blocks.1342        /// 1343        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1344        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1345        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1346        /// from contract endowment if there are at least B blocks between such transactions. 1347        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1348        /// 1349        /// # Permissions1350        /// 1351        /// * Contract Owner1352        /// 1353        /// # Arguments1354        /// 1355        /// -`contract_address`: Address of the contract to sponsor1356        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1357        /// 1358        #[weight = 0]1359        pub fn set_contract_sponsoring_rate_limit(1360            origin,1361            contract_address: T::AccountId,1362            rate_limit: T::BlockNumber1363        ) -> DispatchResult {1364            let sender = ensure_signed(origin)?;1365            let mut is_owner = false;1366            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1367                let owner = <ContractOwner<T>>::get(&contract_address);1368                is_owner = sender == owner;1369            }1370            ensure!(is_owner, Error::<T>::NoPermission);13711372            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1373            Ok(())1374        }13751376    }1377}13781379impl<T: Trait> Module<T> {13801381    fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {13821383        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1384            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1385            Self::check_white_list(collection_id, owner)?;1386            Self::check_white_list(collection_id, sender)?;1387        }13881389        Ok(())1390    }13911392    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1393        match target_collection.mode1394        {1395            CollectionMode::NFT => {1396                if let CreateItemData::NFT(data) = data {1397                    // check sizes1398                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1399                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1400                } else {1401                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1402                }1403            },1404            CollectionMode::Fungible(_) => {1405                if let CreateItemData::Fungible(_) = data {1406                } else {1407                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1408                }1409            },1410            CollectionMode::ReFungible(_) => {1411                if let CreateItemData::ReFungible(data) = data {14121413                    // check sizes1414                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1415                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1416                } else {1417                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1418                }1419            },1420            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1421        };14221423        Ok(())1424    }14251426    fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1427        match data1428        {1429            CreateItemData::NFT(data) => {1430                let item = NftItemType {1431                    collection: collection_id,1432                    owner,1433                    const_data: data.const_data,1434                    variable_data: data.variable_data1435                };14361437                Self::add_nft_item(item)?;1438            },1439            CreateItemData::Fungible(_) => {1440                let item = FungibleItemType {1441                    collection: collection_id,1442                    owner,1443                    value: (10 as u128).pow(collection.decimal_points)1444                };14451446                Self::add_fungible_item(item)?;1447            },1448            CreateItemData::ReFungible(data) => {1449                let mut owner_list = Vec::new();1450                let value = (10 as u128).pow(collection.decimal_points);1451                owner_list.push(Ownership {owner: owner.clone(), fraction: value});14521453                let item = ReFungibleItemType {1454                    collection: collection_id,1455                    owner: owner_list,1456                    const_data: data.const_data,1457                    variable_data: data.variable_data1458                };14591460                Self::add_refungible_item(item)?;1461            }1462        };146314641465        // call event1466        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));14671468        Ok(())1469    }14701471    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1472        let current_index = <ItemListIndex>::get(item.collection)1473            .checked_add(1)1474            .ok_or(Error::<T>::NumOverflow)?;1475        let itemcopy = item.clone();1476        let owner = item.owner.clone();1477        let value = item.value as u64;14781479        Self::add_token_index(item.collection, current_index, owner.clone())?;14801481        <ItemListIndex>::insert(item.collection, current_index);1482        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14831484        // Add current block1485        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1486        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1487        1488        // Update balance1489        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1490            .checked_add(value)1491            .ok_or(Error::<T>::NumOverflow)?;1492        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14931494        Ok(())1495    }14961497    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1498        let current_index = <ItemListIndex>::get(item.collection)1499            .checked_add(1)1500            .ok_or(Error::<T>::NumOverflow)?;1501        let itemcopy = item.clone();15021503        let value = item.owner.first().unwrap().fraction as u64;1504        let owner = item.owner.first().unwrap().owner.clone();15051506        Self::add_token_index(item.collection, current_index, owner.clone())?;15071508        <ItemListIndex>::insert(item.collection, current_index);1509        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15101511        // Add current block1512        let block_number: T::BlockNumber = 0.into();1513        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);15141515        // Update balance1516        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1517            .checked_add(value)1518            .ok_or(Error::<T>::NumOverflow)?;1519        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15201521        Ok(())1522    }15231524    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1525        let current_index = <ItemListIndex>::get(item.collection)1526            .checked_add(1)1527            .ok_or(Error::<T>::NumOverflow)?;15281529        let item_owner = item.owner.clone();1530        let collection_id = item.collection.clone();1531        Self::add_token_index(collection_id, current_index, item.owner.clone())?;15321533        <ItemListIndex>::insert(collection_id, current_index);1534        <NftItemList<T>>::insert(collection_id, current_index, item);15351536        // Add current block1537        let block_number: T::BlockNumber = 0.into();1538        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);15391540        // Update balance1541        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1542            .checked_add(1)1543            .ok_or(Error::<T>::NumOverflow)?;1544        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);15451546        Ok(())1547    }15481549    fn burn_refungible_item(1550        collection_id: u64,1551        item_id: u64,1552        owner: T::AccountId,1553    ) -> DispatchResult {1554        ensure!(1555            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1556            Error::<T>::TokenNotFound1557        );1558        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1559        let item = collection1560            .owner1561            .iter()1562            .filter(|&i| i.owner == owner)1563            .next()1564            .unwrap();1565        Self::remove_token_index(collection_id, item_id, owner.clone())?;15661567        // remove approve list1568        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));15691570        // update balance1571        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1572            .checked_sub(item.fraction as u64)1573            .ok_or(Error::<T>::NumOverflow)?;1574        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15751576        <ReFungibleItemList<T>>::remove(collection_id, item_id);15771578        Ok(())1579    }15801581    fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1582        ensure!(1583            <NftItemList<T>>::contains_key(collection_id, item_id),1584            Error::<T>::TokenNotFound1585        );1586        let item = <NftItemList<T>>::get(collection_id, item_id);1587        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15881589        // remove approve list1590        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15911592        // update balance1593        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1594            .checked_sub(1)1595            .ok_or(Error::<T>::NumOverflow)?;1596        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1597        <NftItemList<T>>::remove(collection_id, item_id);15981599        Ok(())1600    }16011602    fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1603        ensure!(1604            <FungibleItemList<T>>::contains_key(collection_id, item_id),1605            Error::<T>::TokenNotFound1606        );1607        let item = <FungibleItemList<T>>::get(collection_id, item_id);1608        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16091610        // remove approve list1611        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16121613        // update balance1614        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1615            .checked_sub(item.value as u64)1616            .ok_or(Error::<T>::NumOverflow)?;1617        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);16181619        <FungibleItemList<T>>::remove(collection_id, item_id);16201621        Ok(())1622    }16231624    fn collection_exists(collection_id: u64) -> DispatchResult {1625        ensure!(1626            <Collection<T>>::contains_key(collection_id),1627            Error::<T>::CollectionNotFound1628        );1629        Ok(())1630    }16311632    fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1633        Self::collection_exists(collection_id)?;16341635        let target_collection = <Collection<T>>::get(collection_id);1636        ensure!(1637            subject == target_collection.owner,1638            Error::<T>::NoPermission1639        );16401641        Ok(())1642    }16431644    fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1645        let target_collection = <Collection<T>>::get(collection_id);1646        let mut result: bool = subject == target_collection.owner;1647        let exists = <AdminList<T>>::contains_key(collection_id);16481649        if !result & exists {1650            if <AdminList<T>>::get(collection_id).contains(&subject) {1651                result = true1652            }1653        }16541655        result1656    }16571658    fn check_owner_or_admin_permissions(1659        collection_id: u64,1660        subject: T::AccountId,1661    ) -> DispatchResult {1662        Self::collection_exists(collection_id)?;1663        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());16641665        ensure!(1666            result,1667            Error::<T>::NoPermission1668        );1669        Ok(())1670    }16711672    fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1673        let target_collection = <Collection<T>>::get(collection_id);16741675        match target_collection.mode {1676            CollectionMode::NFT => {1677                <NftItemList<T>>::get(collection_id, item_id).owner == subject1678            }1679            CollectionMode::Fungible(_) => {1680                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1681            }1682            CollectionMode::ReFungible(_) => {1683                <ReFungibleItemList<T>>::get(collection_id, item_id)1684                    .owner1685                    .iter()1686                    .any(|i| i.owner == subject)1687            }1688            CollectionMode::Invalid => false,1689        }1690    }16911692    fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1693        let mes = Error::<T>::AddresNotInWhiteList;1694        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1695        let wl = <WhiteList<T>>::get(collection_id);1696        ensure!(wl.contains(address), mes);16971698        Ok(())1699    }17001701    fn transfer_fungible(1702        collection_id: u64,1703        item_id: u64,1704        value: u64,1705        owner: T::AccountId,1706        new_owner: T::AccountId,1707    ) -> DispatchResult {1708        ensure!(1709            <FungibleItemList<T>>::contains_key(collection_id, item_id),1710            Error::<T>::TokenNotFound1711        );17121713        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1714        let amount = full_item.value;17151716        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);17171718        // update balance1719        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1720            .checked_sub(value)1721            .ok_or(Error::<T>::NumOverflow)?;1722        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);17231724        let mut new_owner_account_id = 0;1725        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1726        if new_owner_items.len() > 0 {1727            new_owner_account_id = new_owner_items[0];1728        }17291730        let val64 = value.into();17311732        // transfer1733        if amount == val64 && new_owner_account_id == 0 {1734            // change owner1735            // new owner do not have account1736            let mut new_full_item = full_item.clone();1737            new_full_item.owner = new_owner.clone();1738            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17391740            // update balance1741            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1742                .checked_add(value)1743                .ok_or(Error::<T>::NumOverflow)?;1744            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17451746            // update index collection1747            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1748        } else {1749            let mut new_full_item = full_item.clone();1750            new_full_item.value -= val64;17511752            // separate amount1753            if new_owner_account_id > 0 {1754                // new owner has account1755                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1756                item.value += val64;17571758                // update balance1759                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1760                    .checked_add(value)1761                    .ok_or(Error::<T>::NumOverflow)?;1762                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17631764                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1765            } else {1766                // new owner do not have account1767                let item = FungibleItemType {1768                    collection: collection_id,1769                    owner: new_owner.clone(),1770                    value: val64,1771                };17721773                Self::add_fungible_item(item)?;1774            }17751776            if amount == val64 {1777                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;17781779                // remove approve list1780                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1781                <FungibleItemList<T>>::remove(collection_id, item_id);1782            }17831784            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1785        }17861787        Ok(())1788    }17891790    fn transfer_refungible(1791        collection_id: u64,1792        item_id: u64,1793        value: u64,1794        owner: T::AccountId,1795        new_owner: T::AccountId,1796    ) -> DispatchResult {1797        ensure!(1798            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1799            Error::<T>::TokenNotFound1800        );18011802        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1803        let item = full_item1804            .owner1805            .iter()1806            .filter(|i| i.owner == owner)1807            .next()1808            .ok_or(Error::<T>::NumOverflow)?;1809        let amount = item.fraction;18101811        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);18121813        // update balance1814        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1815            .checked_sub(value)1816            .ok_or(Error::<T>::NumOverflow)?;1817        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18181819        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1820            .checked_add(value)1821            .ok_or(Error::<T>::NumOverflow)?;1822        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18231824        let old_owner = item.owner.clone();1825        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1826        let val64 = value.into();18271828        // transfer1829        if amount == val64 && !new_owner_has_account {1830            // change owner1831            // new owner do not have account1832            let mut new_full_item = full_item.clone();1833            new_full_item1834                .owner1835                .iter_mut()1836                .find(|i| i.owner == owner)1837                .unwrap()1838                .owner = new_owner.clone();1839            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18401841            // update index collection1842            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1843        } else {1844            let mut new_full_item = full_item.clone();1845            new_full_item1846                .owner1847                .iter_mut()1848                .find(|i| i.owner == owner)1849                .unwrap()1850                .fraction -= val64;18511852            // separate amount1853            if new_owner_has_account {1854                // new owner has account1855                new_full_item1856                    .owner1857                    .iter_mut()1858                    .find(|i| i.owner == new_owner)1859                    .unwrap()1860                    .fraction += val64;1861            } else {1862                // new owner do not have account1863                new_full_item.owner.push(Ownership {1864                    owner: new_owner.clone(),1865                    fraction: val64,1866                });1867                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1868            }18691870            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1871        }18721873        Ok(())1874    }18751876    fn transfer_nft(1877        collection_id: u64,1878        item_id: u64,1879        sender: T::AccountId,1880        new_owner: T::AccountId,1881    ) -> DispatchResult {1882        ensure!(1883            <NftItemList<T>>::contains_key(collection_id, item_id),1884            Error::<T>::TokenNotFound1885        );18861887        let mut item = <NftItemList<T>>::get(collection_id, item_id);18881889        ensure!(1890            sender == item.owner,1891            Error::<T>::MustBeTokenOwner1892        );18931894        // update balance1895        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1896            .checked_sub(1)1897            .ok_or(Error::<T>::NumOverflow)?;1898        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18991900        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1901            .checked_add(1)1902            .ok_or(Error::<T>::NumOverflow)?;1903        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19041905        // change owner1906        let old_owner = item.owner.clone();1907        item.owner = new_owner.clone();1908        <NftItemList<T>>::insert(collection_id, item_id, item);19091910        // update index collection1911        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;19121913        // reset approved list1914        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1915        Ok(())1916    }1917    1918    fn item_exists(1919        collection_id: u64,1920        item_id: u64,1921        mode: &CollectionMode1922    ) -> DispatchResult {1923        match mode {1924            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1925            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1926            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1927            _ => ()1928        };1929        1930        Ok(())1931    }19321933    fn set_re_fungible_variable_data(1934        collection_id: u64,1935        item_id: u64,1936        data: Vec<u8>1937    ) -> DispatchResult {1938        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);19391940        item.variable_data = data;19411942        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);19431944        Ok(())1945    }19461947    fn set_nft_variable_data(1948        collection_id: u64,1949        item_id: u64,1950        data: Vec<u8>1951    ) -> DispatchResult {1952        let mut item = <NftItemList<T>>::get(collection_id, item_id);1953        1954        item.variable_data = data;19551956        <NftItemList<T>>::insert(collection_id, item_id, item);1957        1958        Ok(())1959    }19601961    fn init_collection(item: &CollectionType<T::AccountId>) {1962        // check params1963        assert!(1964            item.decimal_points <= 4,1965            "decimal_points parameter must be lower than 4"1966        );1967        assert!(1968            item.name.len() <= 64,1969            "Collection name can not be longer than 63 char"1970        );1971        assert!(1972            item.name.len() <= 256,1973            "Collection description can not be longer than 255 char"1974        );1975        assert!(1976            item.token_prefix.len() <= 16,1977            "Token prefix can not be longer than 15 char"1978        );19791980        // Generate next collection ID1981        let next_id = CreatedCollectionCount::get()1982            .checked_add(1)1983            .unwrap();19841985        CreatedCollectionCount::put(next_id);1986    }19871988    fn init_nft_token(item: &NftItemType<T::AccountId>) {1989        let current_index = <ItemListIndex>::get(item.collection)1990            .checked_add(1)1991            .unwrap();19921993        let item_owner = item.owner.clone();1994        let collection_id = item.collection.clone();1995        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();19961997        <ItemListIndex>::insert(collection_id, current_index);19981999        // Update balance2000        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2001            .checked_add(1)2002            .unwrap();2003        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2004    }20052006    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2007        let current_index = <ItemListIndex>::get(item.collection)2008            .checked_add(1)2009            .unwrap();2010        let owner = item.owner.clone();2011        let value = item.value as u64;20122013        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20142015        <ItemListIndex>::insert(item.collection, current_index);20162017        // Update balance2018        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2019            .checked_add(value)2020            .unwrap();2021        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2022    }20232024    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2025        let current_index = <ItemListIndex>::get(item.collection)2026            .checked_add(1)2027            .unwrap();20282029        let value = item.owner.first().unwrap().fraction as u64;2030        let owner = item.owner.first().unwrap().owner.clone();20312032        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20332034        <ItemListIndex>::insert(item.collection, current_index);20352036        // Update balance2037        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2038            .checked_add(value)2039            .unwrap();2040        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2041    }20422043    fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {20442045        // add to account limit2046        if <AccountItemCount<T>>::contains_key(owner.clone()) {20472048            // bound Owned tokens by a single address2049            let count = <AccountItemCount<T>>::get(owner.clone());2050            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);20512052            <AccountItemCount<T>>::insert(owner.clone(), count2053                .checked_add(1)2054                .ok_or(Error::<T>::NumOverflow)?);2055        }2056        else {2057            <AccountItemCount<T>>::insert(owner.clone(), 1);2058        }20592060        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2061        if list_exists {2062            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2063            let item_contains = list.contains(&item_index.clone());20642065            if !item_contains {2066                list.push(item_index.clone());2067            }20682069            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2070        } else {2071            let mut itm = Vec::new();2072            itm.push(item_index.clone());2073            <AddressTokens<T>>::insert(collection_id, owner, itm);2074            2075        }20762077        Ok(())2078    }20792080    fn remove_token_index(2081        collection_id: u64,2082        item_index: u64,2083        owner: T::AccountId,2084    ) -> DispatchResult {20852086        // update counter2087        <AccountItemCount<T>>::insert(owner.clone(), 2088            <AccountItemCount<T>>::get(owner.clone())2089            .checked_sub(1)2090            .ok_or(Error::<T>::NumOverflow)?);209120922093        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2094        if list_exists {2095            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2096            let item_contains = list.contains(&item_index.clone());20972098            if item_contains {2099                list.retain(|&item| item != item_index);2100                <AddressTokens<T>>::insert(collection_id, owner, list);2101            }2102        }21032104        Ok(())2105    }21062107    fn move_token_index(2108        collection_id: u64,2109        item_index: u64,2110        old_owner: T::AccountId,2111        new_owner: T::AccountId,2112    ) -> DispatchResult {2113        Self::remove_token_index(collection_id, item_index, old_owner)?;2114        Self::add_token_index(collection_id, item_index, new_owner)?;21152116        Ok(())2117    }2118}21192120////////////////////////////////////////////////////////////////////////////////////////////////////2121// Economic models2122// #region21232124/// Fee multiplier.2125pub type Multiplier = FixedU128;21262127type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2128    <T as system::Trait>::AccountId,2129>>::Balance;2130type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2131    <T as system::Trait>::AccountId,2132>>::NegativeImbalance;21332134/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2135/// in the queue.2136#[derive(Encode, Decode, Clone, Eq, PartialEq)]2137pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2138    #[codec(compact)] BalanceOf<T>2139);21402141impl<T: Trait + Send + Sync> sp_std::fmt::Debug2142    for ChargeTransactionPayment<T>2143{2144    #[cfg(feature = "std")]2145    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2146        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2147    }2148    #[cfg(not(feature = "std"))]2149    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2150        Ok(())2151    }2152}21532154impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2155where2156    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2157    BalanceOf<T>: Send + Sync + FixedPointOperand,2158{2159    /// utility constructor. Used only in client/factory code.2160    pub fn from(fee: BalanceOf<T>) -> Self {2161        Self(fee)2162    }21632164    pub fn traditional_fee(2165        len: usize,2166        info: &DispatchInfoOf<T::Call>,2167        tip: BalanceOf<T>,2168    ) -> BalanceOf<T>2169    where2170        T::Call: Dispatchable<Info = DispatchInfo>,2171    {2172        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2173    }21742175    fn withdraw_fee(2176        &self,2177        who: &T::AccountId,2178        call: &T::Call,2179        info: &DispatchInfoOf<T::Call>,2180        len: usize,2181    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2182        let tip = self.0;21832184        // Set fee based on call type. Creating collection costs 1 Unique.2185        // All other transactions have traditional fees so far2186        // let fee = match call.is_sub_type() {2187        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2188        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2189        //                                                 // _ => <BalanceOf<T>>::from(100)2190        // };2191        let fee = Self::traditional_fee(len, info, tip);21922193        // Determine who is paying transaction fee based on ecnomic model2194        // Parse call to extract collection ID and access collection sponsor2195        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2196            Some(Call::create_item(collection_id, _properties, _owner)) => {2197                <Collection<T>>::get(collection_id).sponsor2198            }2199            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2200                let _collection_mode = <Collection<T>>::get(collection_id).mode;22012202                // sponsor timeout2203                let sponsor_transfer = match _collection_mode {2204                    CollectionMode::NFT => {2205                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2206                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2207                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2208                        if block_number >= limit_time {2209                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2210                            true2211                        }2212                        else {2213                            false2214                        }2215                    }2216                    CollectionMode::Fungible(_) => {2217                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2218                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2219                        if basket.iter().any(|i| i.address == _new_owner.clone())2220                        {2221                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2222                            let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2223                            if block_number >= limit_time {2224                                basket.retain(|x| x.address == item.address);2225                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2226                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2227                                true2228                            }2229                            else {2230                                false2231                            }2232                        }2233                        else {2234                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2235                            true2236                        }2237                    }2238                    CollectionMode::ReFungible(_) => {2239                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2240                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2241                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2242                        if block_number >= limit_time {2243                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2244                            true2245                        } else {2246                            false2247                        }2248                    }2249                    _ => {2250                        false2251                    },2252                };22532254                if !sponsor_transfer {2255                    T::AccountId::default()2256                } else {2257                    <Collection<T>>::get(collection_id).sponsor2258                }2259            }22602261            _ => T::AccountId::default(),2262        };22632264        // Sponsor smart contracts2265        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {22662267            // On instantiation: set the contract owner2268            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {22692270                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2271                    code_hash,2272                    &data,2273                    &who,2274                );2275                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());22762277                T::AccountId::default()2278            },22792280            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2281            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {22822283                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());22842285                let mut sponsor_transfer = false;2286                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2287                    let last_tx_block = <ContractSponsorBasket<T>>::get(&called_contract);2288                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2289                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2290                    let limit_time = last_tx_block + rate_limit;22912292                    if block_number >= limit_time {2293                        <ContractSponsorBasket<T>>::insert(called_contract.clone(), block_number);2294                        sponsor_transfer = true;2295                    }2296                } else {2297                    sponsor_transfer = false;2298                }2299               2300                2301                let mut sp = T::AccountId::default();2302                if sponsor_transfer {2303                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2304                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2305                            sp = called_contract;2306                        }2307                    }2308                }23092310                sp2311            },23122313            _ => sponsor,2314        };23152316        let mut who_pays_fee: T::AccountId = sponsor.clone();2317        if sponsor == T::AccountId::default() {2318            who_pays_fee = who.clone();2319        }23202321        // Only mess with balances if fee is not zero.2322        if fee.is_zero() {2323            return Ok((fee, None));2324        }23252326        match <T as transaction_payment::Trait>::Currency::withdraw(2327            &who_pays_fee,2328            fee,2329            if tip.is_zero() {2330                WithdrawReason::TransactionPayment.into()2331            } else {2332                WithdrawReason::TransactionPayment | WithdrawReason::Tip2333            },2334            ExistenceRequirement::KeepAlive,2335        ) {2336            Ok(imbalance) => Ok((fee, Some(imbalance))),2337            Err(_) => Err(InvalidTransaction::Payment.into()),2338        }2339    }2340}234123422343impl<T: Trait + Send + Sync> SignedExtension2344    for ChargeTransactionPayment<T>2345where2346    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2347    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2348{2349    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2350    type AccountId = T::AccountId;2351    type Call = T::Call;2352    type AdditionalSigned = ();2353    type Pre = (2354        BalanceOf<T>,2355        Self::AccountId,2356        Option<NegativeImbalanceOf<T>>,2357        BalanceOf<T>,2358    );2359    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2360        Ok(())2361    }23622363    fn validate(2364        &self,2365        _who: &Self::AccountId,2366        _call: &Self::Call,2367        _info: &DispatchInfoOf<Self::Call>,2368        _len: usize,2369    ) -> TransactionValidity {2370        Ok(ValidTransaction::default())2371    }23722373    fn pre_dispatch(2374        self,2375        who: &Self::AccountId,2376        call: &Self::Call,2377        info: &DispatchInfoOf<Self::Call>,2378        len: usize,2379    ) -> Result<Self::Pre, TransactionValidityError> {2380        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2381        Ok((self.0, who.clone(), imbalance, fee))2382    }23832384    fn post_dispatch(2385        pre: Self::Pre,2386        info: &DispatchInfoOf<Self::Call>,2387        post_info: &PostDispatchInfoOf<Self::Call>,2388        len: usize,2389        _result: &DispatchResult,2390    ) -> Result<(), TransactionValidityError> {2391        let (tip, who, imbalance, fee) = pre;2392        if let Some(payed) = imbalance {2393            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2394                len as u32, info, post_info, tip,2395            );2396            let refund = fee.saturating_sub(actual_fee);2397            let actual_payment =2398                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2399                    &who, refund,2400                ) {2401                    Ok(refund_imbalance) => {2402                        // The refund cannot be larger than the up front payed max weight.2403                        // `PostDispatchInfo::calc_unspent` guards against such a case.2404                        match payed.offset(refund_imbalance) {2405                            Ok(actual_payment) => actual_payment,2406                            Err(_) => return Err(InvalidTransaction::Payment.into()),2407                        }2408                    }2409                    // We do not recreate the account using the refund. The up front payment2410                    // is gone in that case.2411                    Err(_) => payed,2412                };2413            let imbalances = actual_payment.split(tip);2414            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2415                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2416            );2417        }2418        Ok(())2419    }2420}24212422// #endregion
modifiedpallets/nft/src/tests.rsdiffbeforeafterboth
--- a/pallets/nft/src/tests.rs
+++ b/pallets/nft/src/tests.rs
@@ -1,6 +1,8 @@
 // Tests to be written here
+use super::*;
 use crate::mock::*;
-use crate::{AccessMode, ApprovePermissions, CollectionMode, Ownership, ChainLimits, CreateItemData, CreateNftData, CreateFungibleData, CreateReFungibleData};
+use crate::{AccessMode, ApprovePermissions, CollectionMode,
+     Ownership, ChainLimits, CreateItemData, CreateNftData, CreateFungibleData, CreateReFungibleData}; //Err
 use frame_support::{assert_noop, assert_ok};
 use frame_system::{ RawOrigin };
 
@@ -392,7 +394,7 @@
             2,
             1,
             1,
-            1), "Only item owner, collection owner and admins can modify items");
+            1), Error::<Test>::NoPermission);
 
         // do approve
         assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
@@ -672,7 +674,7 @@
         assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1));
         assert_noop!(
             TemplateModule::burn_item(origin1.clone(), 1, 1),
-            "Item does not exists"
+            Error::<Test>::TokenNotFound
         );
 
         assert_eq!(TemplateModule::balance_count(1, 1), 0);
@@ -699,7 +701,7 @@
         assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1));
         assert_noop!(
             TemplateModule::burn_item(origin1.clone(), 1, 1),
-            "Item does not exists"
+            Error::<Test>::TokenNotFound
         );
 
         assert_eq!(TemplateModule::balance_count(1, 1), 0);
@@ -738,7 +740,7 @@
         assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1));
         assert_noop!(
             TemplateModule::burn_item(origin1.clone(), 1, 1),
-            "Item does not exists"
+            Error::<Test>::TokenNotFound
         );
 
         assert_eq!(TemplateModule::balance_count(1, 1), 0);
@@ -933,7 +935,7 @@
         let origin2 = Origin::signed(2);
         assert_noop!(
             TemplateModule::add_to_white_list(origin2.clone(), collection_id, 3),
-            "You do not have permissions to modify this collection"
+            Error::<Test>::NoPermission
         );
     });
 }
@@ -947,7 +949,7 @@
 
         assert_noop!(
             TemplateModule::add_to_white_list(origin1.clone(), 1, 2),
-            "This collection does not exist"
+            Error::<Test>::CollectionNotFound
         );
     });
 }
@@ -963,7 +965,7 @@
         assert_ok!(TemplateModule::destroy_collection(origin1.clone(), collection_id));
         assert_noop!(
             TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2),
-            "This collection does not exist"
+            Error::<Test>::CollectionNotFound
         );
     });
 }
@@ -1035,7 +1037,7 @@
         assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
         assert_noop!(
             TemplateModule::remove_from_white_list(origin2.clone(), collection_id, 2),
-            "You do not have permissions to modify this collection"
+            Error::<Test>::NoPermission
         );
         assert_eq!(TemplateModule::white_list(collection_id)[0], 2);
     });
@@ -1049,7 +1051,7 @@
 
         assert_noop!(
             TemplateModule::remove_from_white_list(origin1.clone(), 1, 2),
-            "This collection does not exist"
+            Error::<Test>::CollectionNotFound
         );
     });
 }
@@ -1067,7 +1069,7 @@
         assert_ok!(TemplateModule::destroy_collection(origin1.clone(), collection_id));
         assert_noop!(
             TemplateModule::remove_from_white_list(origin2.clone(), collection_id, 2),
-            "This collection does not exist"
+            Error::<Test>::CollectionNotFound
         );
         assert_eq!(TemplateModule::white_list(collection_id).len(), 0);
     });
@@ -1119,7 +1121,7 @@
 
         assert_noop!(
             TemplateModule::transfer(origin1.clone(), 3, 1, 1, 1),
-            "Address is not in white list"
+            Error::<Test>::AddresNotInWhiteList
         );
     });
 }
@@ -1155,7 +1157,7 @@
 
         assert_noop!(
             TemplateModule::transfer_from(origin1.clone(), 1, 3, 1, 1, 1),
-            "Address is not in white list"
+            Error::<Test>::AddresNotInWhiteList
         );
     });
 }
@@ -1182,7 +1184,7 @@
 
         assert_noop!(
             TemplateModule::transfer(origin1.clone(), 3, 1, 1, 1),
-            "Address is not in white list"
+            Error::<Test>::AddresNotInWhiteList
         );
     });
 }
@@ -1219,7 +1221,7 @@
 
         assert_noop!(
             TemplateModule::transfer_from(origin1.clone(), 1, 3, 1, 1, 1),
-            "Address is not in white list"
+            Error::<Test>::AddresNotInWhiteList
         );
     });
 }
@@ -1244,7 +1246,7 @@
         ));
         assert_noop!(
             TemplateModule::burn_item(origin1.clone(), 1, 1),
-            "Address is not in white list"
+            Error::<Test>::AddresNotInWhiteList
         );
     });
 }
@@ -1267,7 +1269,7 @@
         // do approve
         assert_noop!(
             TemplateModule::approve(origin1.clone(), 1, 1, 1),
-            "Address is not in white list"
+            Error::<Test>::AddresNotInWhiteList
         );
     });
 }
@@ -1416,7 +1418,7 @@
 
         assert_noop!(
             TemplateModule::create_item(origin2.clone(), 1, 2, default_nft_data().into()),
-            "Public minting is not allowed for this collection"
+            Error::<Test>::PublicMintingNotAllowed
         );
     });
 }
@@ -1445,7 +1447,7 @@
 
         assert_noop!(
             TemplateModule::create_item(origin2.clone(), 1, 2, default_nft_data().into()),
-            "Public minting is not allowed for this collection"
+            Error::<Test>::PublicMintingNotAllowed
         );
     });
 }
@@ -1533,7 +1535,7 @@
 
         assert_noop!(
             TemplateModule::create_item(origin2.clone(), 1, 2, default_nft_data().into()),
-            "Address is not in white list"
+            Error::<Test>::AddresNotInWhiteList
         );
     });
 }
@@ -1603,7 +1605,7 @@
             col_desc1.clone(),
             token_prefix1.clone(),
             CollectionMode::NFT
-        ), "Total collections bound exceeded");
+        ), Error::<Test>::TotalCollectionsLimitExceeded);
     });
 }
 
@@ -1646,7 +1648,7 @@
             1,
             1,
             data.into()
-        ), "Owned tokens by a single address bound exceeded");
+        ),  Error::<Test>::AddressOwnershipLimitExceeded);
     });
 }
 
@@ -1692,7 +1694,7 @@
         let origin1 = Origin::signed(1);
 
         assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 2));
-        assert_noop!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 3), "Number of collection admins bound exceeded");
+        assert_noop!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 3), Error::<Test>::CollectionAdminsLimitExceeded);
     });
 }
 
modifiedruntime/src/nft_weights.rsdiffbeforeafterboth
--- a/runtime/src/nft_weights.rs
+++ b/runtime/src/nft_weights.rs
@@ -108,4 +108,19 @@
             .saturating_add(DbWeight::get().reads(2 as Weight))
             .saturating_add(DbWeight::get().writes(1 as Weight))
     }
+    // fn set_chain_limits() -> Weight {
+    //     (0 as Weight)
+    //         .saturating_add(DbWeight::get().reads(1 as Weight))
+    //         .saturating_add(DbWeight::get().writes(1 as Weight))
+    // }
+    // fn enable_contract_sponsoring() -> Weight {
+    //     (0 as Weight)
+    //         .saturating_add(DbWeight::get().reads(1 as Weight))
+    //         .saturating_add(DbWeight::get().writes(1 as Weight))
+    // }
+    // fn set_contract_sponsoring_rate_limit() -> Weight {
+    //     (0 as Weight)
+    //         .saturating_add(DbWeight::get().reads(1 as Weight))
+    //         .saturating_add(DbWeight::get().writes(1 as Weight))
+    // }
 }