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

difftreelog

Cleanup approve tests

Greg Zaitsev2021-02-26parent: #bdf204a.patch.diff
in: master

2 files changed

modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
before · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, IsSubType,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial, DispatchClass,29    },30    StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use sp_runtime::traits::StaticLookup;45use pallet_contracts::chain_extension::UncheckedFrom;46use transaction_payment::OnChargeTransaction;4748#[cfg(test)]49mod mock;5051#[cfg(test)]52mod tests;5354mod default_weights;5556pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;57pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;58pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;59pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6061// Structs62// #region6364pub type CollectionId = u32;65pub type TokenId = u32;66pub type DecimalPoints = u8;6768#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]69#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]70pub enum CollectionMode {71    Invalid,72    NFT,73    // decimal points74    Fungible(DecimalPoints),75    ReFungible,76}7778impl Default for CollectionMode {79    fn default() -> Self {80        Self::Invalid81    }82}8384impl Into<u8> for CollectionMode {85    fn into(self) -> u8 {86        match self {87            CollectionMode::Invalid => 0,88            CollectionMode::NFT => 1,89            CollectionMode::Fungible(_) => 2,90            CollectionMode::ReFungible => 3,91        }92    }93}9495#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]96#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]97pub enum AccessMode {98    Normal,99    WhiteList,100}101impl Default for AccessMode {102    fn default() -> Self {103        Self::Normal104    }105}106107#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]108#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]109pub enum SchemaVersion {110    ImageURL,111    Unique,112}113impl Default for SchemaVersion {114    fn default() -> Self {115        Self::ImageURL116    }117}118119#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]120#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]121pub struct Ownership<AccountId> {122    pub owner: AccountId,123    pub fraction: u128,124}125126#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub struct CollectionType<AccountId> {129    pub owner: AccountId,130    pub mode: CollectionMode,131    pub access: AccessMode,132    pub decimal_points: DecimalPoints,133    pub name: Vec<u16>,        // 64 include null escape char134    pub description: Vec<u16>, // 256 include null escape char135    pub token_prefix: Vec<u8>, // 16 include null escape char136    pub mint_mode: bool,137    pub offchain_schema: Vec<u8>,138    pub schema_version: SchemaVersion,139    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender140    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.141    pub limits: CollectionLimits, // Collection private restrictions 142    pub variable_on_chain_schema: Vec<u8>, //143    pub const_on_chain_schema: Vec<u8>, //144}145146#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]147#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]148pub struct NftItemType<AccountId> {149    pub owner: AccountId,150    pub const_data: Vec<u8>,151    pub variable_data: Vec<u8>,152}153154#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]155#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]156pub struct FungibleItemType {157    pub value: u128,158}159160#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]161#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]162pub struct ReFungibleItemType<AccountId> {163    pub owner: Vec<Ownership<AccountId>>,164    pub const_data: Vec<u8>,165    pub variable_data: Vec<u8>,166}167168// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]169// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170// pub struct VestingItem<AccountId, Moment> {171//     pub sender: AccountId,172//     pub recipient: AccountId,173//     pub collection_id: CollectionId,174//     pub item_id: TokenId,175//     pub amount: u64,176//     pub vesting_date: Moment,177// }178179#[derive(Encode, Decode, Debug, Clone, PartialEq)]180#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]181pub struct CollectionLimits {182    pub account_token_ownership_limit: u32,183    pub sponsored_data_size: u32,184    pub token_limit: u32,185186    // Timeouts for item types in passed blocks187    pub sponsor_transfer_timeout: u32,188    pub owner_can_transfer: bool,189    pub owner_can_destroy: bool,190}191192impl Default for CollectionLimits {193    fn default() -> CollectionLimits {194        CollectionLimits { 195            account_token_ownership_limit: 10_000_000, 196            token_limit: u32::max_value(),197            sponsored_data_size: u32::max_value(), 198            sponsor_transfer_timeout: 14400,199            owner_can_transfer: true,200            owner_can_destroy: true201        }202    }203}204205#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]206#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]207pub struct ChainLimits {208    pub collection_numbers_limit: u32,209    pub account_token_ownership_limit: u32,210    pub collections_admins_limit: u64,211    pub custom_data_limit: u32,212213    // Timeouts for item types in passed blocks214    pub nft_sponsor_transfer_timeout: u32,215    pub fungible_sponsor_transfer_timeout: u32,216    pub refungible_sponsor_transfer_timeout: u32,217218    // Schema limits219    pub offchain_schema_limit: u32,220    pub variable_on_chain_schema_limit: u32,221    pub const_on_chain_schema_limit: u32,222}223224pub trait WeightInfo {225	fn create_collection() -> Weight;226	fn destroy_collection() -> Weight;227	fn add_to_white_list() -> Weight;228	fn remove_from_white_list() -> Weight;229    fn set_public_access_mode() -> Weight;230    fn set_mint_permission() -> Weight;231    fn change_collection_owner() -> Weight;232    fn add_collection_admin() -> Weight;233    fn remove_collection_admin() -> Weight;234    fn set_collection_sponsor() -> Weight;235    fn confirm_sponsorship() -> Weight;236    fn remove_collection_sponsor() -> Weight;237    fn create_item(s: usize) -> Weight;238    fn burn_item() -> Weight;239    fn transfer() -> Weight;240    fn approve() -> Weight;241    fn transfer_from() -> Weight;242    fn set_offchain_schema() -> Weight;243    fn set_const_on_chain_schema() -> Weight;244    fn set_variable_on_chain_schema() -> Weight;245    fn set_variable_meta_data() -> Weight;246    fn enable_contract_sponsoring() -> Weight;247    fn set_schema_version() -> Weight;248    fn set_chain_limits() -> Weight;249    fn set_contract_sponsoring_rate_limit() -> Weight;250    fn toggle_contract_white_list() -> Weight;251    fn add_to_contract_white_list() -> Weight;252    fn remove_from_contract_white_list() -> Weight;253    fn set_collection_limits() -> Weight;254}255256#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]257#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]258pub struct CreateNftData {259    pub const_data: Vec<u8>,260    pub variable_data: Vec<u8>,261}262263#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]264#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]265pub struct CreateFungibleData {266    pub value: u128,267}268269#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]270#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]271pub struct CreateReFungibleData {272    pub const_data: Vec<u8>,273    pub variable_data: Vec<u8>,274    pub pieces: u128,275}276277#[derive(Encode, Decode, Debug, Clone, PartialEq)]278#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]279pub enum CreateItemData {280    NFT(CreateNftData),281    Fungible(CreateFungibleData),282    ReFungible(CreateReFungibleData),283}284285impl CreateItemData {286    pub fn len(&self) -> usize {287        let len = match self {288            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),289            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),290            _ => 0291        };292        293        return len;294    }295}296297impl From<CreateNftData> for CreateItemData {298    fn from(item: CreateNftData) -> Self {299        CreateItemData::NFT(item)300    }301}302303impl From<CreateReFungibleData> for CreateItemData {304    fn from(item: CreateReFungibleData) -> Self {305        CreateItemData::ReFungible(item)306    }307}308309impl From<CreateFungibleData> for CreateItemData {310    fn from(item: CreateFungibleData) -> Self {311        CreateItemData::Fungible(item)312    }313}314315316decl_error! {317	/// Error for non-fungible-token module.318	pub enum Error for Module<T: Config> {319        /// Total collections bound exceeded.320        TotalCollectionsLimitExceeded,321		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.322        CollectionDecimalPointLimitExceeded, 323        /// Collection name can not be longer than 63 char.324        CollectionNameLimitExceeded, 325        /// Collection description can not be longer than 255 char.326        CollectionDescriptionLimitExceeded, 327        /// Token prefix can not be longer than 15 char.328        CollectionTokenPrefixLimitExceeded,329        /// This collection does not exist.330        CollectionNotFound,331        /// Item not exists.332        TokenNotFound,333        /// Admin not found334        AdminNotFound,335        /// Arithmetic calculation overflow.336        NumOverflow,       337        /// Account already has admin role.338        AlreadyAdmin,  339        /// You do not own this collection.340        NoPermission,341        /// This address is not set as sponsor, use setCollectionSponsor first.342        ConfirmUnsetSponsorFail,343        /// Collection is not in mint mode.344        PublicMintingNotAllowed,345        /// Sender parameter and item owner must be equal.346        MustBeTokenOwner,347        /// Item balance not enough.348        TokenValueTooLow,349        /// Size of item is too large.350        NftSizeLimitExceeded,351        /// Owned amount is lesser than tried to approve352        CantAfford,353        /// No approve found354        ApproveNotFound,355        /// Requested value more than approved.356        TokenValueNotEnough,357        /// Only approved addresses can call this method.358        ApproveRequired,359        /// Address is not in white list.360        AddresNotInWhiteList,361        /// Number of collection admins bound exceeded.362        CollectionAdminsLimitExceeded,363        /// Owned tokens by a single address bound exceeded.364        AddressOwnershipLimitExceeded,365        /// Length of items properties must be greater than 0.366        EmptyArgument,367        /// const_data exceeded data limit.368        TokenConstDataLimitExceeded,369        /// variable_data exceeded data limit.370        TokenVariableDataLimitExceeded,371        /// Not NFT item data used to mint in NFT collection.372        NotNftDataUsedToMintNftCollectionToken,373        /// Not Fungible item data used to mint in Fungible collection.374        NotFungibleDataUsedToMintFungibleCollectionToken,375        /// Not Re Fungible item data used to mint in Re Fungible collection.376        NotReFungibleDataUsedToMintReFungibleCollectionToken,377        /// Unexpected collection type.378        UnexpectedCollectionType,379        /// Can't store metadata in fungible tokens.380        CantStoreMetadataInFungibleTokens,381        /// Collection token limit exceeded382        CollectionTokenLimitExceeded,383        /// Account token limit exceeded per collection384        AccountTokenLimitExceeded,385        /// Collection limit bounds per collection exceeded386        CollectionLimitBoundsExceeded,387        /// Schema data size limit bound exceeded388        SchemaDataLimitExceeded,389        /// Maximum refungibility exceeded390        WrongRefungiblePieces391	}392}393394pub trait Config: system::Config + Sized + transaction_payment::Config + pallet_contracts::Config {395    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;396397    /// Weight information for extrinsics in this pallet.398	type WeightInfo: WeightInfo;399}400401#[cfg(feature = "runtime-benchmarks")]402mod benchmarking;403404// #endregion405406decl_storage! {407    trait Store for Module<T: Config> as Nft {408409        // Private members410        NextCollectionID: CollectionId;411        CreatedCollectionCount: u32;412        ChainVersion: u64;413        ItemListIndex: map hasher(identity) CollectionId => TokenId;414415        // Chain limits struct416        pub ChainLimit get(fn chain_limit) config(): ChainLimits;417418        // Bound counters419        CollectionCount: u32;420        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;421422        // Basic collections423        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;424        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;425        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;426427        /// Balance owner per collection map428        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;429430        /// second parameter: item id + owner account id + spender account id431        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;432433        /// Item collections434        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;435        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;436        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;437438        /// Index list439        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;440441        /// Tokens transfer baskets442        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;443        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;444        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;445        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;446447        // Contract Sponsorship and Ownership448        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;449        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;450        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;451        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;452        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 453        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 454    }455    add_extra_genesis {456        build(|config: &GenesisConfig<T>| {457            // Modification of storage458            for (_num, _c) in &config.collection {459                <Module<T>>::init_collection(_c);460            }461462            for (_num, _c, _i) in &config.nft_item_id {463                <Module<T>>::init_nft_token(*_c, _i);464            }465466            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {467                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);468            }469470            for (_num, _c, _i) in &config.refungible_item_id {471                <Module<T>>::init_refungible_token(*_c, _i);472            }473        })474    }475}476477decl_event!(478    pub enum Event<T>479    where480        AccountId = <T as system::Config>::AccountId,481    {482        /// New collection was created483        /// 484        /// # Arguments485        /// 486        /// * collection_id: Globally unique identifier of newly created collection.487        /// 488        /// * mode: [CollectionMode] converted into u8.489        /// 490        /// * account_id: Collection owner.491        Created(CollectionId, u8, AccountId),492493        /// New item was created.494        /// 495        /// # Arguments496        /// 497        /// * collection_id: Id of the collection where item was created.498        /// 499        /// * item_id: Id of an item. Unique within the collection.500        ItemCreated(CollectionId, TokenId),501502        /// Collection item was burned.503        /// 504        /// # Arguments505        /// 506        /// collection_id.507        /// 508        /// item_id: Identifier of burned NFT.509        ItemDestroyed(CollectionId, TokenId),510    }511);512513decl_module! {514    pub struct Module<T: Config> for enum Call 515    where 516        origin: T::Origin517    {518        fn deposit_event() = default;519        type Error = Error<T>;520521        fn on_initialize(now: T::BlockNumber) -> Weight {522523            if ChainVersion::get() < 2524            {525                let value = NextCollectionID::get();526                CreatedCollectionCount::put(value);527                ChainVersion::put(2);528            }529530            0531        }532533        /// 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.534        /// 535        /// # Permissions536        /// 537        /// * Anyone.538        /// 539        /// # Arguments540        /// 541        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.542        /// 543        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.544        /// 545        /// * token_prefix: UTF-8 string with token prefix.546        /// 547        /// * mode: [CollectionMode] collection type and type dependent data.548        // returns collection ID549        #[weight = <T as Config>::WeightInfo::create_collection()]550        pub fn create_collection(origin,551                                 collection_name: Vec<u16>,552                                 collection_description: Vec<u16>,553                                 token_prefix: Vec<u8>,554                                 mode: CollectionMode) -> DispatchResult {555556            // Anyone can create a collection557            let who = ensure_signed(origin)?;558559            let decimal_points = match mode {560                CollectionMode::Fungible(points) => points,561                _ => 0562            };563564            // bound Total number of collections565            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);566567            // check params568            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);569            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);570            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);571            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);572573            // Generate next collection ID574            let next_id = CreatedCollectionCount::get()575                .checked_add(1)576                .ok_or(Error::<T>::NumOverflow)?;577578            // bound counter579            let total = CollectionCount::get()580                .checked_add(1)581                .ok_or(Error::<T>::NumOverflow)?;582583            CreatedCollectionCount::put(next_id);584            CollectionCount::put(total);585586            // Create new collection587            let new_collection = CollectionType {588                owner: who.clone(),589                name: collection_name,590                mode: mode.clone(),591                mint_mode: false,592                access: AccessMode::Normal,593                description: collection_description,594                decimal_points: decimal_points,595                token_prefix: token_prefix,596                offchain_schema: Vec::new(),597                schema_version: SchemaVersion::ImageURL,598                sponsor: T::AccountId::default(),599                sponsor_confirmed: false,600                variable_on_chain_schema: Vec::new(),601                const_on_chain_schema: Vec::new(),602                limits: CollectionLimits::default()603            };604605            // Add new collection to map606            <Collection<T>>::insert(next_id, new_collection);607608            // call event609            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));610611            Ok(())612        }613614        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.615        /// 616        /// # Permissions617        /// 618        /// * Collection Owner.619        /// 620        /// # Arguments621        /// 622        /// * collection_id: collection to destroy.623        #[weight = <T as Config>::WeightInfo::destroy_collection()]624        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {625626            let sender = ensure_signed(origin)?;627            Self::check_owner_permissions(collection_id, sender)?;628629            <AddressTokens<T>>::remove_prefix(collection_id);630            <Allowances<T>>::remove_prefix(collection_id);631            <Balance<T>>::remove_prefix(collection_id);632            <ItemListIndex>::remove(collection_id);633            <AdminList<T>>::remove(collection_id);634            <Collection<T>>::remove(collection_id);635            <WhiteList<T>>::remove_prefix(collection_id);636637            <NftItemList<T>>::remove_prefix(collection_id);638            <FungibleItemList<T>>::remove_prefix(collection_id);639            <ReFungibleItemList<T>>::remove_prefix(collection_id);640641            <NftTransferBasket<T>>::remove_prefix(collection_id);642            <FungibleTransferBasket<T>>::remove_prefix(collection_id);643            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);644645            if CollectionCount::get() > 0646            {647                // bound couter648                let total = CollectionCount::get()649                    .checked_sub(1)650                    .ok_or(Error::<T>::NumOverflow)?;651652                CollectionCount::put(total);653            }654655            Ok(())656        }657658        /// Add an address to white list.659        /// 660        /// # Permissions661        /// 662        /// * Collection Owner663        /// * Collection Admin664        /// 665        /// # Arguments666        /// 667        /// * collection_id.668        /// 669        /// * address.670        #[weight = <T as Config>::WeightInfo::add_to_white_list()]671        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{672673            let sender = ensure_signed(origin)?;674            Self::check_owner_or_admin_permissions(collection_id, sender)?;675676            <WhiteList<T>>::insert(collection_id, address, true);677            678            Ok(())679        }680681        /// Remove an address from white list.682        /// 683        /// # Permissions684        /// 685        /// * Collection Owner686        /// * Collection Admin687        /// 688        /// # Arguments689        /// 690        /// * collection_id.691        /// 692        /// * address.693        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]694        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{695696            let sender = ensure_signed(origin)?;697            Self::check_owner_or_admin_permissions(collection_id, sender)?;698699            <WhiteList<T>>::remove(collection_id, address);700701            Ok(())702        }703704        /// Toggle between normal and white list access for the methods with access for `Anyone`.705        /// 706        /// # Permissions707        /// 708        /// * Collection Owner.709        /// 710        /// # Arguments711        /// 712        /// * collection_id.713        /// 714        /// * mode: [AccessMode]715        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]716        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult717        {718            let sender = ensure_signed(origin)?;719720            Self::check_owner_permissions(collection_id, sender)?;721            let mut target_collection = <Collection<T>>::get(collection_id);722            target_collection.access = mode;723            <Collection<T>>::insert(collection_id, target_collection);724725            Ok(())726        }727728        /// Allows Anyone to create tokens if:729        /// * White List is enabled, and730        /// * Address is added to white list, and731        /// * This method was called with True parameter732        /// 733        /// # Permissions734        /// * Collection Owner735        ///736        /// # Arguments737        /// 738        /// * collection_id.739        /// 740        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.741        #[weight = <T as Config>::WeightInfo::set_mint_permission()]742        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult743        {744            let sender = ensure_signed(origin)?;745746            Self::check_owner_permissions(collection_id, sender)?;747            let mut target_collection = <Collection<T>>::get(collection_id);748            target_collection.mint_mode = mint_permission;749            <Collection<T>>::insert(collection_id, target_collection);750751            Ok(())752        }753754        /// Change the owner of the collection.755        /// 756        /// # Permissions757        /// 758        /// * Collection Owner.759        /// 760        /// # Arguments761        /// 762        /// * collection_id.763        /// 764        /// * new_owner.765        #[weight = <T as Config>::WeightInfo::change_collection_owner()]766        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {767768            let sender = ensure_signed(origin)?;769            Self::check_owner_permissions(collection_id, sender)?;770            let mut target_collection = <Collection<T>>::get(collection_id);771            target_collection.owner = new_owner;772            <Collection<T>>::insert(collection_id, target_collection);773774            Ok(())775        }776777        /// Adds an admin of the Collection.778        /// 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. 779        /// 780        /// # Permissions781        /// 782        /// * Collection Owner.783        /// * Collection Admin.784        /// 785        /// # Arguments786        /// 787        /// * collection_id: ID of the Collection to add admin for.788        /// 789        /// * new_admin_id: Address of new admin to add.790        #[weight = <T as Config>::WeightInfo::add_collection_admin()]791        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {792793            let sender = ensure_signed(origin)?;794            Self::check_owner_or_admin_permissions(collection_id, sender)?;795            let mut admin_arr: Vec<T::AccountId> = Vec::new();796797            if <AdminList<T>>::contains_key(collection_id)798            {799                admin_arr = <AdminList<T>>::get(collection_id);800                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);801            }802803            // Number of collection admins804            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);805806            admin_arr.push(new_admin_id);807            <AdminList<T>>::insert(collection_id, admin_arr);808809            Ok(())810        }811812        /// 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.813        ///814        /// # Permissions815        /// 816        /// * Collection Owner.817        /// * Collection Admin.818        /// 819        /// # Arguments820        /// 821        /// * collection_id: ID of the Collection to remove admin for.822        /// 823        /// * account_id: Address of admin to remove.824        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]825        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {826827            let sender = ensure_signed(origin)?;828            Self::check_owner_or_admin_permissions(collection_id, sender)?;829            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);830831            let mut admin_arr = <AdminList<T>>::get(collection_id);832            admin_arr.retain(|i| *i != account_id);833            <AdminList<T>>::insert(collection_id, admin_arr);834835            Ok(())836        }837838        /// # Permissions839        /// 840        /// * Collection Owner841        /// 842        /// # Arguments843        /// 844        /// * collection_id.845        /// 846        /// * new_sponsor.847        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]848        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {849850            let sender = ensure_signed(origin)?;851            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);852853            let mut target_collection = <Collection<T>>::get(collection_id);854            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);855856            target_collection.sponsor = new_sponsor;857            target_collection.sponsor_confirmed = false;858            <Collection<T>>::insert(collection_id, target_collection);859860            Ok(())861        }862863        /// # Permissions864        /// 865        /// * Sponsor.866        /// 867        /// # Arguments868        /// 869        /// * collection_id.870        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]871        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {872873            let sender = ensure_signed(origin)?;874            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);875876            let mut target_collection = <Collection<T>>::get(collection_id);877            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);878879            target_collection.sponsor_confirmed = true;880            <Collection<T>>::insert(collection_id, target_collection);881882            Ok(())883        }884885        /// Switch back to pay-per-own-transaction model.886        ///887        /// # Permissions888        ///889        /// * Collection owner.890        /// 891        /// # Arguments892        /// 893        /// * collection_id.894        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]895        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {896897            let sender = ensure_signed(origin)?;898            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);899900            let mut target_collection = <Collection<T>>::get(collection_id);901            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);902903            target_collection.sponsor = T::AccountId::default();904            target_collection.sponsor_confirmed = false;905            <Collection<T>>::insert(collection_id, target_collection);906907            Ok(())908        }909910        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.911        /// 912        /// # Permissions913        /// 914        /// * Collection Owner.915        /// * Collection Admin.916        /// * Anyone if917        ///     * White List is enabled, and918        ///     * Address is added to white list, and919        ///     * MintPermission is enabled (see SetMintPermission method)920        /// 921        /// # Arguments922        /// 923        /// * collection_id: ID of the collection.924        /// 925        /// * owner: Address, initial owner of the NFT.926        ///927        /// * data: Token data to store on chain.928        // #[weight =929        // (130_000_000 as Weight)930        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))931        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))932        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]933934        #[weight = <T as Config>::WeightInfo::create_item(data.len())]935        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {936937            let sender = ensure_signed(origin)?;938939            Self::collection_exists(collection_id)?;940941            let target_collection = <Collection<T>>::get(collection_id);942943            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;944            Self::validate_create_item_args(&target_collection, &data)?;945            Self::create_item_no_validation(collection_id, owner, data)?;946947            Ok(())948        }949950        /// This method creates multiple instances of NFT Collection created with CreateCollection method.951        /// 952        /// # Permissions953        /// 954        /// * Collection Owner.955        /// * Collection Admin.956        /// * Anyone if957        ///     * White List is enabled, and958        ///     * Address is added to white list, and959        ///     * MintPermission is enabled (see SetMintPermission method)960        /// 961        /// # Arguments962        /// 963        /// * collection_id: ID of the collection.964        /// 965        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].966        /// 967        /// * owner: Address, initial owner of the NFT.968        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()969                               .map(|data| { data.len() })970                               .sum())]971        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {972973            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);974            let sender = ensure_signed(origin)?;975976            Self::collection_exists(collection_id)?;977            let target_collection = <Collection<T>>::get(collection_id);978979            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;980981            for data in &items_data {982                Self::validate_create_item_args(&target_collection, data)?;983            }984            for data in &items_data {985                Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?;986            }987988            Ok(())989        }990991        /// Destroys a concrete instance of NFT.992        /// 993        /// # Permissions994        /// 995        /// * Collection Owner.996        /// * Collection Admin.997        /// * Current NFT Owner.998        /// 999        /// # Arguments1000        /// 1001        /// * collection_id: ID of the collection.1002        /// 1003        /// * item_id: ID of NFT to burn.1004        #[weight = <T as Config>::WeightInfo::burn_item()]1005        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10061007            let sender = ensure_signed(origin)?;1008            Self::collection_exists(collection_id)?;10091010            // Transfer permissions check1011            let target_collection = <Collection<T>>::get(collection_id);1012            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1013                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1014                Error::<T>::NoPermission);10151016            if target_collection.access == AccessMode::WhiteList {1017                Self::check_white_list(collection_id, &sender)?;1018            }10191020            match target_collection.mode1021            {1022                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1023                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1024                CollectionMode::ReFungible  => Self::burn_refungible_item(collection_id, item_id, &sender)?,1025                _ => ()1026            };10271028            // call event1029            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10301031            Ok(())1032        }10331034        /// Change ownership of the token.1035        /// 1036        /// # Permissions1037        /// 1038        /// * Collection Owner1039        /// * Collection Admin1040        /// * Current NFT owner1041        ///1042        /// # Arguments1043        /// 1044        /// * recipient: Address of token recipient.1045        /// 1046        /// * collection_id.1047        /// 1048        /// * item_id: ID of the item1049        ///     * Non-Fungible Mode: Required.1050        ///     * Fungible Mode: Ignored.1051        ///     * Re-Fungible Mode: Required.1052        /// 1053        /// * value: Amount to transfer.1054        ///     * Non-Fungible Mode: Ignored1055        ///     * Fungible Mode: Must specify transferred amount1056        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1057        #[weight = <T as Config>::WeightInfo::transfer()]1058        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1059            let sender = ensure_signed(origin)?;1060            Self::transfer_internal(sender, recipient, collection_id, item_id, value)1061        }10621063        /// Set, change, or remove approved address to transfer the ownership of the NFT.1064        /// 1065        /// # Permissions1066        /// 1067        /// * Collection Owner1068        /// * Collection Admin1069        /// * Current NFT owner1070        /// 1071        /// # Arguments1072        /// 1073        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1074        /// 1075        /// * collection_id.1076        /// 1077        /// * item_id: ID of the item.1078        #[weight = <T as Config>::WeightInfo::approve()]1079        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {10801081            let sender = ensure_signed(origin)?;10821083            Self::collection_exists(collection_id)?;1084            Self::token_exists(collection_id, item_id, &sender)?;10851086            // Transfer permissions check1087            let target_collection = <Collection<T>>::get(collection_id);1088            let allowance_limit = if Self::is_owner_or_admin_permissions(1089                collection_id,1090                sender.clone(),1091            ) {1092                None1093            } else if let Some(amount) = Self::owned_amount(1094                sender.clone(),1095                collection_id,1096                item_id,1097            ) {1098                Some(amount)1099            } else {1100                fail!(Error::<T>::NoPermission);1101            };11021103            if target_collection.access == AccessMode::WhiteList {1104                Self::check_white_list(collection_id, &sender)?;1105                Self::check_white_list(collection_id, &spender)?;1106            }11071108            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1109            let mut allowance: u128 = amount;1110            if allowance_exists {1111                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1112            }1113            if let Some(limit) = allowance_limit {1114                ensure!(limit >= allowance, Error::<T>::CantAfford);1115            }1116            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11171118            Ok(())1119        }1120        1121        /// 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.1122        /// 1123        /// # Permissions1124        /// * Collection Owner1125        /// * Collection Admin1126        /// * Current NFT owner1127        /// * Address approved by current NFT owner1128        /// 1129        /// # Arguments1130        /// 1131        /// * from: Address that owns token.1132        /// 1133        /// * recipient: Address of token recipient.1134        /// 1135        /// * collection_id.1136        /// 1137        /// * item_id: ID of the item.1138        /// 1139        /// * value: Amount to transfer.1140        #[weight = <T as Config>::WeightInfo::transfer_from()]1141        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11421143            let sender = ensure_signed(origin)?;1144            let mut appoved_transfer = false;11451146            // Check approval1147            let mut approval: u128 = 0;1148            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1149                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1150                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1151                appoved_transfer = true;1152            }11531154            let target_collection = <Collection<T>>::get(collection_id);11551156            // Limits check1157            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11581159            // Transfer permissions check         1160            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1161                Error::<T>::NoPermission);11621163            if target_collection.access == AccessMode::WhiteList {1164                Self::check_white_list(collection_id, &sender)?;1165                Self::check_white_list(collection_id, &recipient)?;1166            }11671168            // Reduce approval by transferred amount or remove if remaining approval drops to 01169            if approval.checked_sub(value).unwrap_or(0) > 0 {1170                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1171            }1172            else {1173                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1174            }11751176            match target_collection.mode1177            {1178                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1179                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1180                CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1181                _ => ()1182            };11831184            Ok(())1185        }11861187        // #[weight = 0]1188        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11891190        //     // let no_perm_mes = "You do not have permissions to modify this collection";1191        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1192        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1193        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11941195        //     // // on_nft_received  call11961197        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;11981199        //     Ok(())1200        // }12011202        /// Set off-chain data schema.1203        /// 1204        /// # Permissions1205        /// 1206        /// * Collection Owner1207        /// * Collection Admin1208        /// 1209        /// # Arguments1210        /// 1211        /// * collection_id.1212        /// 1213        /// * schema: String representing the offchain data schema.1214        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1215        pub fn set_variable_meta_data (1216            origin,1217            collection_id: CollectionId,1218            item_id: TokenId,1219            data: Vec<u8>1220        ) -> DispatchResult {1221            let sender = ensure_signed(origin)?;1222            1223            Self::collection_exists(collection_id)?;1224            Self::token_exists(collection_id, item_id, &sender)?;12251226            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12271228            // Modify permissions check1229            let target_collection = <Collection<T>>::get(collection_id);1230            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1231                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1232                Error::<T>::NoPermission);12331234            match target_collection.mode1235            {1236                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1237                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1238                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1239                _ => fail!(Error::<T>::UnexpectedCollectionType)1240            };12411242            Ok(())1243        }1244 1245        /// Set schema standard1246        /// ImageURL1247        /// Unique1248        /// 1249        /// # Permissions1250        /// 1251        /// * Collection Owner1252        /// * Collection Admin1253        /// 1254        /// # Arguments1255        /// 1256        /// * collection_id.1257        /// 1258        /// * schema: SchemaVersion: enum1259        #[weight = <T as Config>::WeightInfo::set_schema_version()]1260        pub fn set_schema_version(1261            origin,1262            collection_id: CollectionId,1263            version: SchemaVersion1264        ) -> DispatchResult {1265            let sender = ensure_signed(origin)?;1266            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1267            let mut target_collection = <Collection<T>>::get(collection_id);1268            target_collection.schema_version = version;1269            <Collection<T>>::insert(collection_id, target_collection);12701271            Ok(())1272        }12731274        /// Set off-chain data schema.1275        /// 1276        /// # Permissions1277        /// 1278        /// * Collection Owner1279        /// * Collection Admin1280        /// 1281        /// # Arguments1282        /// 1283        /// * collection_id.1284        /// 1285        /// * schema: String representing the offchain data schema.1286        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1287        pub fn set_offchain_schema(1288            origin,1289            collection_id: CollectionId,1290            schema: Vec<u8>1291        ) -> DispatchResult {1292            let sender = ensure_signed(origin)?;1293            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12941295            // check schema limit1296            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");12971298            let mut target_collection = <Collection<T>>::get(collection_id);1299            target_collection.offchain_schema = schema;1300            <Collection<T>>::insert(collection_id, target_collection);13011302            Ok(())1303        }13041305        /// Set const on-chain data schema.1306        /// 1307        /// # Permissions1308        /// 1309        /// * Collection Owner1310        /// * Collection Admin1311        /// 1312        /// # Arguments1313        /// 1314        /// * collection_id.1315        /// 1316        /// * schema: String representing the const on-chain data schema.1317        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1318        pub fn set_const_on_chain_schema (1319            origin,1320            collection_id: CollectionId,1321            schema: Vec<u8>1322        ) -> DispatchResult {1323            let sender = ensure_signed(origin)?;1324            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13251326            // check schema limit1327            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");13281329            let mut target_collection = <Collection<T>>::get(collection_id);1330            target_collection.const_on_chain_schema = schema;1331            <Collection<T>>::insert(collection_id, target_collection);13321333            Ok(())1334        }13351336        /// Set variable on-chain data schema.1337        /// 1338        /// # Permissions1339        /// 1340        /// * Collection Owner1341        /// * Collection Admin1342        /// 1343        /// # Arguments1344        /// 1345        /// * collection_id.1346        /// 1347        /// * schema: String representing the variable on-chain data schema.1348        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1349        pub fn set_variable_on_chain_schema (1350            origin,1351            collection_id: CollectionId,1352            schema: Vec<u8>1353        ) -> DispatchResult {1354            let sender = ensure_signed(origin)?;1355            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13561357            // check schema limit1358            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");13591360            let mut target_collection = <Collection<T>>::get(collection_id);1361            target_collection.variable_on_chain_schema = schema;1362            <Collection<T>>::insert(collection_id, target_collection);13631364            Ok(())1365        }13661367        // Sudo permissions function1368        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1369        pub fn set_chain_limits(1370            origin,1371            limits: ChainLimits1372        ) -> DispatchResult {13731374            #[cfg(not(feature = "runtime-benchmarks"))]1375            ensure_root(origin)?;13761377            <ChainLimit>::put(limits);1378            Ok(())1379        }13801381        /// Enable smart contract self-sponsoring.1382        /// 1383        /// # Permissions1384        /// 1385        /// * Contract Owner1386        /// 1387        /// # Arguments1388        /// 1389        /// * contract address1390        /// * enable flag1391        /// 1392        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1393        pub fn enable_contract_sponsoring(1394            origin,1395            contract_address: T::AccountId,1396            enable: bool1397        ) -> DispatchResult {13981399            let sender = ensure_signed(origin)?;14001401            #[cfg(feature = "runtime-benchmarks")]1402            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14031404            Self::ensure_contract_owned(sender, &contract_address)?;14051406            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1407            Ok(())1408        }14091410        /// Set the rate limit for contract sponsoring to specified number of blocks.1411        /// 1412        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1413        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1414        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1415        /// from contract endowment if there are at least B blocks between such transactions. 1416        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1417        /// 1418        /// # Permissions1419        /// 1420        /// * Contract Owner1421        /// 1422        /// # Arguments1423        /// 1424        /// -`contract_address`: Address of the contract to sponsor1425        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1426        /// 1427        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1428        pub fn set_contract_sponsoring_rate_limit(1429            origin,1430            contract_address: T::AccountId,1431            rate_limit: T::BlockNumber1432        ) -> DispatchResult {1433            let sender = ensure_signed(origin)?;14341435            #[cfg(feature = "runtime-benchmarks")]1436            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14371438            Self::ensure_contract_owned(sender, &contract_address)?;1439            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1440            Ok(())1441        }14421443        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1444        /// 1445        /// # Permissions1446        /// 1447        /// * Address that deployed smart contract.1448        /// 1449        /// # Arguments1450        /// 1451        /// -`contract_address`: Address of the contract.1452        /// 1453        /// - `enable`: .  1454        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1455        pub fn toggle_contract_white_list(1456            origin,1457            contract_address: T::AccountId,1458            enable: bool1459        ) -> DispatchResult {1460            let sender = ensure_signed(origin)?;14611462            #[cfg(feature = "runtime-benchmarks")]1463            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14641465            Self::ensure_contract_owned(sender, &contract_address)?;1466            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1467            Ok(())1468        }1469        1470        /// Add an address to smart contract white list.1471        /// 1472        /// # Permissions1473        /// 1474        /// * Address that deployed smart contract.1475        /// 1476        /// # Arguments1477        /// 1478        /// -`contract_address`: Address of the contract.1479        ///1480        /// -`account_address`: Address to add.1481        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1482        pub fn add_to_contract_white_list(1483            origin,1484            contract_address: T::AccountId,1485            account_address: T::AccountId1486        ) -> DispatchResult {1487            let sender = ensure_signed(origin)?;14881489            #[cfg(feature = "runtime-benchmarks")]1490            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1491            1492            Self::ensure_contract_owned(sender, &contract_address)?;      1493            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1494            Ok(())1495        }14961497        /// Remove an address from smart contract white list.1498        /// 1499        /// # Permissions1500        /// 1501        /// * Address that deployed smart contract.1502        /// 1503        /// # Arguments1504        /// 1505        /// -`contract_address`: Address of the contract.1506        ///1507        /// -`account_address`: Address to remove.1508        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1509        pub fn remove_from_contract_white_list(1510            origin,1511            contract_address: T::AccountId,1512            account_address: T::AccountId1513        ) -> DispatchResult {1514            let sender = ensure_signed(origin)?;15151516            #[cfg(feature = "runtime-benchmarks")]1517            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15181519            Self::ensure_contract_owned(sender, &contract_address)?;1520            <ContractWhiteList<T>>::remove(contract_address, account_address);1521            Ok(())1522        }15231524        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1525        pub fn set_collection_limits(1526            origin,1527            collection_id: u32,1528            limits: CollectionLimits,1529        ) -> DispatchResult {1530            let sender = ensure_signed(origin)?;1531            Self::check_owner_permissions(collection_id, sender.clone())?;1532            let mut target_collection = <Collection<T>>::get(collection_id);1533            let chain_limits = ChainLimit::get();1534            let climits = target_collection.limits;15351536            // collection bounds1537            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1538                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1539                Error::<T>::CollectionLimitBoundsExceeded);15401541            // token_limit   check  prev1542            ensure!(climits.token_limit > limits.token_limit && 1543                limits.token_limit <= chain_limits.account_token_ownership_limit, 1544                Error::<T>::AccountTokenLimitExceeded);15451546            target_collection.limits = limits;1547            <Collection<T>>::insert(collection_id, target_collection);15481549            Ok(())1550        } 1551    }1552}15531554impl<T: Config> Module<T> {15551556    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {15571558        let target_collection = <Collection<T>>::get(collection_id);15591560        // Limits check1561        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;15621563        // Transfer permissions check1564        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1565            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1566            Error::<T>::NoPermission);15671568        if target_collection.access == AccessMode::WhiteList {1569            Self::check_white_list(collection_id, &sender)?;1570            Self::check_white_list(collection_id, &recipient)?;1571        }15721573        match target_collection.mode1574        {1575            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1576            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1577            CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1578            _ => ()1579        };15801581        Ok(())1582    }158315841585    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15861587        // check token limit and account token limit1588        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1589        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1590        1591        Ok(())1592    }15931594    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15951596        // check token limit and account token limit1597        let total_items: u32 = ItemListIndex::get(collection_id);1598        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1599        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1600        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);16011602        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1603            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1604            Self::check_white_list(collection_id, owner)?;1605            Self::check_white_list(collection_id, sender)?;1606        }16071608        Ok(())1609    }16101611    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1612        match target_collection.mode1613        {1614            CollectionMode::NFT => {1615                if let CreateItemData::NFT(data) = data {1616                    // check sizes1617                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1618                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1619                } else {1620                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1621                }1622            },1623            CollectionMode::Fungible(_) => {1624                if let CreateItemData::Fungible(_) = data {1625                } else {1626                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1627                }1628            },1629            CollectionMode::ReFungible => {1630                if let CreateItemData::ReFungible(data) = data {16311632                    // check sizes1633                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1634                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16351636                    // Check refungibility limits1637                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1638                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1639                } else {1640                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1641                }1642            },1643            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1644        };16451646        Ok(())1647    }16481649    fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1650        match data1651        {1652            CreateItemData::NFT(data) => {1653                let item = NftItemType {1654                    owner,1655                    const_data: data.const_data,1656                    variable_data: data.variable_data1657                };16581659                Self::add_nft_item(collection_id, item)?;1660            },1661            CreateItemData::Fungible(data) => {1662                Self::add_fungible_item(collection_id, &owner, data.value)?;1663            },1664            CreateItemData::ReFungible(data) => {1665                let mut owner_list = Vec::new();1666                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});16671668                let item = ReFungibleItemType {1669                    owner: owner_list,1670                    const_data: data.const_data,1671                    variable_data: data.variable_data1672                };16731674                Self::add_refungible_item(collection_id, item)?;1675            }1676        };16771678        // call event1679        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16801681        Ok(())1682    }16831684    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {16851686        // Does new owner already have an account?1687        let mut balance: u128 = 0;1688        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1689            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1690        } 16911692        // Mint 1693        let item = FungibleItemType {1694            value: balance + value1695        };1696        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16971698        // Update balance1699        let new_balance = <Balance<T>>::get(collection_id, owner)1700            .checked_add(value)1701            .ok_or(Error::<T>::NumOverflow)?;1702        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17031704        Ok(())1705    }17061707    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1708        let current_index = <ItemListIndex>::get(collection_id)1709            .checked_add(1)1710            .ok_or(Error::<T>::NumOverflow)?;1711        let itemcopy = item.clone();17121713        let value = item.owner.first().unwrap().fraction;1714        let owner = item.owner.first().unwrap().owner.clone();17151716        Self::add_token_index(collection_id, current_index, &owner)?;17171718        <ItemListIndex>::insert(collection_id, current_index);1719        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17201721        // Update balance1722        let new_balance = <Balance<T>>::get(collection_id, &owner)1723            .checked_add(value)1724            .ok_or(Error::<T>::NumOverflow)?;1725        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17261727        Ok(())1728    }17291730    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1731        let current_index = <ItemListIndex>::get(collection_id)1732            .checked_add(1)1733            .ok_or(Error::<T>::NumOverflow)?;17341735        let item_owner = item.owner.clone();1736        Self::add_token_index(collection_id, current_index, &item.owner)?;17371738        <ItemListIndex>::insert(collection_id, current_index);1739        <NftItemList<T>>::insert(collection_id, current_index, item);17401741        // Update balance1742        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1743            .checked_add(1)1744            .ok_or(Error::<T>::NumOverflow)?;1745        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17461747        Ok(())1748    }17491750    fn burn_refungible_item(1751        collection_id: CollectionId,1752        item_id: TokenId,1753        owner: &T::AccountId,1754    ) -> DispatchResult {1755        ensure!(1756            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1757            Error::<T>::TokenNotFound1758        );1759        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1760        let rft_balance = token1761            .owner1762            .iter()1763            .filter(|&i| i.owner == *owner)1764            .next()1765            .unwrap();1766        Self::remove_token_index(collection_id, item_id, owner)?;17671768        // update balance1769        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1770            .checked_sub(rft_balance.fraction)1771            .ok_or(Error::<T>::NumOverflow)?;1772        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);17731774        // Re-create owners list with sender removed1775        let index = token1776            .owner1777            .iter()1778            .position(|i| i.owner == *owner)1779            .unwrap();1780        token.owner.remove(index);1781        let owner_count = token.owner.len();17821783        // Burn the token completely if this was the last (only) owner1784        if owner_count == 0 {1785            <ReFungibleItemList<T>>::remove(collection_id, item_id);1786        }1787        else {1788            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1789        }17901791        Ok(())1792    }17931794    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1795        ensure!(1796            <NftItemList<T>>::contains_key(collection_id, item_id),1797            Error::<T>::TokenNotFound1798        );1799        let item = <NftItemList<T>>::get(collection_id, item_id);1800        Self::remove_token_index(collection_id, item_id, &item.owner)?;18011802        // update balance1803        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1804            .checked_sub(1)1805            .ok_or(Error::<T>::NumOverflow)?;1806        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1807        <NftItemList<T>>::remove(collection_id, item_id);18081809        Ok(())1810    }18111812    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1813        ensure!(1814            <FungibleItemList<T>>::contains_key(collection_id, owner),1815            Error::<T>::TokenNotFound1816        );1817        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1818        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18191820        // update balance1821        let new_balance = <Balance<T>>::get(collection_id, owner)1822            .checked_sub(value)1823            .ok_or(Error::<T>::NumOverflow)?;1824        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18251826        if balance.value - value > 0 {1827            balance.value -= value;1828            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1829        }1830        else {1831            <FungibleItemList<T>>::remove(collection_id, owner);1832        }18331834        Ok(())1835    }18361837    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1838        ensure!(1839            <Collection<T>>::contains_key(collection_id),1840            Error::<T>::CollectionNotFound1841        );1842        Ok(())1843    }18441845    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1846        Self::collection_exists(collection_id)?;18471848        let target_collection = <Collection<T>>::get(collection_id);1849        ensure!(1850            subject == target_collection.owner,1851            Error::<T>::NoPermission1852        );18531854        Ok(())1855    }18561857    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1858        let target_collection = <Collection<T>>::get(collection_id);1859        let mut result: bool = subject == target_collection.owner;1860        let exists = <AdminList<T>>::contains_key(collection_id);18611862        if !result & exists {1863            if <AdminList<T>>::get(collection_id).contains(&subject) {1864                result = true1865            }1866        }18671868        result1869    }18701871    fn check_owner_or_admin_permissions(1872        collection_id: CollectionId,1873        subject: T::AccountId,1874    ) -> DispatchResult {1875        Self::collection_exists(collection_id)?;1876        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18771878        ensure!(1879            result,1880            Error::<T>::NoPermission1881        );1882        Ok(())1883    }18841885    fn owned_amount(1886        subject: T::AccountId,1887        collection_id: CollectionId,1888        item_id: TokenId,1889    ) -> Option<u128> {1890        let target_collection = <Collection<T>>::get(collection_id);18911892        match target_collection.mode {1893            CollectionMode::NFT => {1894                if <NftItemList<T>>::get(collection_id, item_id).owner == subject {1895                    return Some(1)1896                }1897                None1898            },1899            CollectionMode::Fungible(_) => {1900                if <FungibleItemList<T>>::contains_key(collection_id, &subject) {1901                    return Some(<FungibleItemList<T>>::get(collection_id, &subject)1902                        .value);1903                }1904                None1905            },1906            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)1907                .owner1908                .iter()1909                .find(|i| i.owner == subject)1910                .map(|i| i.fraction),1911            CollectionMode::Invalid => None,1912        }1913    }19141915    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1916        let target_collection = <Collection<T>>::get(collection_id);19171918        match target_collection.mode {1919            CollectionMode::NFT => {1920                <NftItemList<T>>::get(collection_id, item_id).owner == subject1921            }1922            CollectionMode::Fungible(_) => {1923                <FungibleItemList<T>>::contains_key(collection_id, &subject)1924            }1925            CollectionMode::ReFungible => {1926                <ReFungibleItemList<T>>::get(collection_id, item_id)1927                    .owner1928                    .iter()1929                    .any(|i| i.owner == subject)1930            }1931            CollectionMode::Invalid => false,1932        }1933    }19341935    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1936        let mes = Error::<T>::AddresNotInWhiteList;1937        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);19381939        Ok(())1940    }19411942    /// Check if token exists. In case of Fungible, check if there is an entry for 1943    /// the owner in fungible balances double map1944    fn token_exists(1945        collection_id: CollectionId,1946        item_id: TokenId,1947        owner: &T::AccountId1948    ) -> DispatchResult {1949        let target_collection = <Collection<T>>::get(collection_id);1950        let exists = match target_collection.mode1951        {1952            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1953            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),1954            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1955            _ => false1956        };19571958        ensure!(exists == true, Error::<T>::TokenNotFound);1959        Ok(())1960    }19611962    fn transfer_fungible(1963        collection_id: CollectionId,1964        value: u128,1965        owner: &T::AccountId,1966        recipient: &T::AccountId,1967    ) -> DispatchResult {1968        Self::token_exists(collection_id, 0, owner)?;19691970        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1971        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19721973        // Send balance to recipient (updates balanceOf of recipient)1974        Self::add_fungible_item(collection_id, recipient, value)?;19751976        // update balanceOf of sender1977        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);19781979        // Reduce or remove sender1980        if balance.value == value {1981            <FungibleItemList<T>>::remove(collection_id, owner);1982        }1983        else {1984            balance.value -= value;1985            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1986        }19871988        Ok(())1989    }19901991    fn transfer_refungible(1992        collection_id: CollectionId,1993        item_id: TokenId,1994        value: u128,1995        owner: T::AccountId,1996        new_owner: T::AccountId,1997    ) -> DispatchResult {1998        Self::token_exists(collection_id, item_id, &owner)?;19992000        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);2001        let item = full_item2002            .owner2003            .iter()2004            .filter(|i| i.owner == owner)2005            .next()2006            .ok_or(Error::<T>::NumOverflow)?;2007        let amount = item.fraction;20082009        ensure!(amount >= value, Error::<T>::TokenValueTooLow);20102011        // update balance2012        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2013            .checked_sub(value)2014            .ok_or(Error::<T>::NumOverflow)?;2015        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20162017        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2018            .checked_add(value)2019            .ok_or(Error::<T>::NumOverflow)?;2020        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20212022        let old_owner = item.owner.clone();2023        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20242025        // transfer2026        if amount == value && !new_owner_has_account {2027            // change owner2028            // new owner do not have account2029            let mut new_full_item = full_item.clone();2030            new_full_item2031                .owner2032                .iter_mut()2033                .find(|i| i.owner == owner)2034                .unwrap()2035                .owner = new_owner.clone();2036            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20372038            // update index collection2039            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2040        } else {2041            let mut new_full_item = full_item.clone();2042            new_full_item2043                .owner2044                .iter_mut()2045                .find(|i| i.owner == owner)2046                .unwrap()2047                .fraction -= value;20482049            // separate amount2050            if new_owner_has_account {2051                // new owner has account2052                new_full_item2053                    .owner2054                    .iter_mut()2055                    .find(|i| i.owner == new_owner)2056                    .unwrap()2057                    .fraction += value;2058            } else {2059                // new owner do not have account2060                new_full_item.owner.push(Ownership {2061                    owner: new_owner.clone(),2062                    fraction: value,2063                });2064                Self::add_token_index(collection_id, item_id, &new_owner)?;2065            }20662067            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2068        }20692070        Ok(())2071    }20722073    fn transfer_nft(2074        collection_id: CollectionId,2075        item_id: TokenId,2076        sender: T::AccountId,2077        new_owner: T::AccountId,2078    ) -> DispatchResult {2079        Self::token_exists(collection_id, item_id, &sender)?;20802081        let mut item = <NftItemList<T>>::get(collection_id, item_id);20822083        ensure!(2084            sender == item.owner,2085            Error::<T>::MustBeTokenOwner2086        );20872088        // update balance2089        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2090            .checked_sub(1)2091            .ok_or(Error::<T>::NumOverflow)?;2092        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20932094        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2095            .checked_add(1)2096            .ok_or(Error::<T>::NumOverflow)?;2097        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20982099        // change owner2100        let old_owner = item.owner.clone();2101        item.owner = new_owner.clone();2102        <NftItemList<T>>::insert(collection_id, item_id, item);21032104        // update index collection2105        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21062107        Ok(())2108    }2109    2110    fn set_re_fungible_variable_data(2111        collection_id: CollectionId,2112        item_id: TokenId,2113        data: Vec<u8>2114    ) -> DispatchResult {2115        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);21162117        item.variable_data = data;21182119        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21202121        Ok(())2122    }21232124    fn set_nft_variable_data(2125        collection_id: CollectionId,2126        item_id: TokenId,2127        data: Vec<u8>2128    ) -> DispatchResult {2129        let mut item = <NftItemList<T>>::get(collection_id, item_id);2130        2131        item.variable_data = data;21322133        <NftItemList<T>>::insert(collection_id, item_id, item);2134        2135        Ok(())2136    }21372138    fn init_collection(item: &CollectionType<T::AccountId>) {2139        // check params2140        assert!(2141            item.decimal_points <= MAX_DECIMAL_POINTS,2142            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2143        );2144        assert!(2145            item.name.len() <= 64,2146            "Collection name can not be longer than 63 char"2147        );2148        assert!(2149            item.name.len() <= 256,2150            "Collection description can not be longer than 255 char"2151        );2152        assert!(2153            item.token_prefix.len() <= 16,2154            "Token prefix can not be longer than 15 char"2155        );21562157        // Generate next collection ID2158        let next_id = CreatedCollectionCount::get()2159            .checked_add(1)2160            .unwrap();21612162        CreatedCollectionCount::put(next_id);2163    }21642165    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2166        let current_index = <ItemListIndex>::get(collection_id)2167            .checked_add(1)2168            .unwrap();21692170        let item_owner = item.owner.clone();2171        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21722173        <ItemListIndex>::insert(collection_id, current_index);21742175        // Update balance2176        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2177            .checked_add(1)2178            .unwrap();2179        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2180    }21812182    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2183        let current_index = <ItemListIndex>::get(collection_id)2184            .checked_add(1)2185            .unwrap();21862187        Self::add_token_index(collection_id, current_index, owner).unwrap();21882189        <ItemListIndex>::insert(collection_id, current_index);21902191        // Update balance2192        let new_balance = <Balance<T>>::get(collection_id, owner)2193            .checked_add(item.value)2194            .unwrap();2195        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2196    }21972198    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2199        let current_index = <ItemListIndex>::get(collection_id)2200            .checked_add(1)2201            .unwrap();22022203        let value = item.owner.first().unwrap().fraction;2204        let owner = item.owner.first().unwrap().owner.clone();22052206        Self::add_token_index(collection_id, current_index, &owner).unwrap();22072208        <ItemListIndex>::insert(collection_id, current_index);22092210        // Update balance2211        let new_balance = <Balance<T>>::get(collection_id, &owner)2212            .checked_add(value)2213            .unwrap();2214        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2215    }22162217    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {22182219        // add to account limit2220        if <AccountItemCount<T>>::contains_key(owner) {22212222            // bound Owned tokens by a single address2223            let count = <AccountItemCount<T>>::get(owner);2224            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);22252226            <AccountItemCount<T>>::insert(owner.clone(), count2227                .checked_add(1)2228                .ok_or(Error::<T>::NumOverflow)?);2229        }2230        else {2231            <AccountItemCount<T>>::insert(owner.clone(), 1);2232        }22332234        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2235        if list_exists {2236            let mut list = <AddressTokens<T>>::get(collection_id, owner);2237            let item_contains = list.contains(&item_index.clone());22382239            if !item_contains {2240                list.push(item_index.clone());2241            }22422243            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2244        } else {2245            let mut itm = Vec::new();2246            itm.push(item_index.clone());2247            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2248        }22492250        Ok(())2251    }22522253    fn remove_token_index(2254        collection_id: CollectionId,2255        item_index: TokenId,2256        owner: &T::AccountId,2257    ) -> DispatchResult {22582259        // update counter2260        <AccountItemCount<T>>::insert(owner.clone(), 2261            <AccountItemCount<T>>::get(owner)2262            .checked_sub(1)2263            .ok_or(Error::<T>::NumOverflow)?);226422652266        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2267        if list_exists {2268            let mut list = <AddressTokens<T>>::get(collection_id, owner);2269            let item_contains = list.contains(&item_index.clone());22702271            if item_contains {2272                list.retain(|&item| item != item_index);2273                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2274            }2275        }22762277        Ok(())2278    }22792280    fn move_token_index(2281        collection_id: CollectionId,2282        item_index: TokenId,2283        old_owner: &T::AccountId,2284        new_owner: &T::AccountId,2285    ) -> DispatchResult {2286        Self::remove_token_index(collection_id, item_index, old_owner)?;2287        Self::add_token_index(collection_id, item_index, new_owner)?;22882289        Ok(())2290    }2291    2292    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2293        if <ContractOwner<T>>::contains_key(contract.clone()) {2294            let owner = <ContractOwner<T>>::get(contract);2295            ensure!(account == owner, Error::<T>::NoPermission);2296        } else {2297            fail!(Error::<T>::NoPermission);2298        }22992300        Ok(())2301    }2302}23032304////////////////////////////////////////////////////////////////////////////////////////////////////2305// Economic models2306// #region23072308/// Fee multiplier.2309pub type Multiplier = FixedU128;23102311type BalanceOf<T> = <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::Balance;23122313/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2314/// in the queue.2315#[derive(Encode, Decode, Clone, Eq, PartialEq)]2316pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);23172318impl<T: Config + Send + Sync> sp_std::fmt::Debug 2319    for ChargeTransactionPayment<T>2320{2321	#[cfg(feature = "std")]2322	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2323		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2324	}2325	#[cfg(not(feature = "std"))]2326	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2327		Ok(())2328	}2329}23302331impl<T: Config> ChargeTransactionPayment<T>2332where2333    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2334    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2335    T::AccountId: AsRef<[u8]>,2336    T::AccountId: UncheckedFrom<T::Hash>,2337{2338    fn traditional_fee(2339        len: usize,2340        info: &DispatchInfoOf<T::Call>,2341        tip: BalanceOf<T>,2342    ) -> BalanceOf<T>2343    where2344        T::Call: Dispatchable<Info = DispatchInfo>,2345    {2346        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2347    }23482349	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2350        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2351        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2352        let len_saturation = max_block_length as u64 / (len as u64).max(1);2353        let coefficient: BalanceOf<T> = weight_saturation2354            .min(len_saturation)2355            .saturated_into::<BalanceOf<T>>();2356        final_fee2357            .saturating_mul(coefficient)2358            .saturated_into::<TransactionPriority>()2359    }23602361    fn withdraw_fee(2362        &self,2363        who: &T::AccountId,2364        call: &T::Call,2365        info: &DispatchInfoOf<T::Call>,2366        len: usize,2367	) -> Result<2368		(2369			BalanceOf<T>,2370			<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2371		),2372		TransactionValidityError,2373	> {2374        let tip = self.0;23752376        // Set fee based on call type. Creating collection costs 1 Unique.2377        // All other transactions have traditional fees so far2378        // let fee = match call.is_sub_type() {2379        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2380        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2381        //                                                 // _ => <BalanceOf<T>>::from(100)2382        // };2383        let fee = Self::traditional_fee(len, info, tip);23842385        // Only mess with balances if fee is not zero.2386        if fee.is_zero() {2387            return <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2388			.map(|i| (fee, i));2389        }23902391        // Determine who is paying transaction fee based on ecnomic model2392        // Parse call to extract collection ID and access collection sponsor2393        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2394            Some(Call::create_item(collection_id, _owner, _properties)) => {23952396                // sponsor timeout2397                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;23982399                let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;2400                let mut sponsored = true;2401                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2402                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2403                    let limit_time = last_tx_block + limit.into();2404                    if block_number <= limit_time {2405                        sponsored = false;2406                    }2407                }2408                if sponsored {2409                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2410                }24112412                // check free create limit2413                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2414                   (<Collection<T>>::get(collection_id).sponsor_confirmed) &&2415                   (sponsored)2416                {2417                    <Collection<T>>::get(collection_id).sponsor2418                } else {2419                    T::AccountId::default()2420                }2421            }2422            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2423                2424                let mut sponsor_transfer = false;2425                if <Collection<T>>::get(collection_id).sponsor_confirmed {24262427                    let collection_limits = <Collection<T>>::get(collection_id).limits;2428                    let collection_mode = <Collection<T>>::get(collection_id).mode;2429    2430                    // sponsor timeout2431                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2432                    sponsor_transfer = match collection_mode {2433                        CollectionMode::NFT => {2434    2435                            // get correct limit2436                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2437                                collection_limits.sponsor_transfer_timeout2438                            } else {2439                                ChainLimit::get().nft_sponsor_transfer_timeout2440                            };2441    2442                            let mut sponsored = true;2443                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2444                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2445                                let limit_time = last_tx_block + limit.into();2446                                if block_number <= limit_time {2447                                    sponsored = false;2448                                }2449                            }2450                            if sponsored {2451                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2452                            }24532454                            sponsored2455                        }2456                        CollectionMode::Fungible(_) => {2457    2458                            // get correct limit2459                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2460                                collection_limits.sponsor_transfer_timeout2461                            } else {2462                                ChainLimit::get().fungible_sponsor_transfer_timeout2463                            };2464    2465                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2466                            let mut sponsored = true;2467                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2468                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2469                                let limit_time = last_tx_block + limit.into();2470                                if block_number <= limit_time {2471                                    sponsored = false;2472                                }2473                            }2474                            if sponsored {2475                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2476                            }24772478                            sponsored2479                        }2480                        CollectionMode::ReFungible => {2481    2482                            // get correct limit2483                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2484                                collection_limits.sponsor_transfer_timeout2485                            } else {2486                                ChainLimit::get().refungible_sponsor_transfer_timeout2487                            };2488    2489                            let mut sponsored = true;2490                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2491                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2492                                let limit_time = last_tx_block + limit.into();2493                                if block_number <= limit_time {2494                                    sponsored = false;2495                                }2496                            }2497                            if sponsored {2498                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2499                            }25002501                            sponsored2502                        }2503                        _ => {2504                            false2505                        },2506                    };2507                }25082509                if !sponsor_transfer {2510                    T::AccountId::default()2511                } else {2512                    <Collection<T>>::get(collection_id).sponsor2513                }2514            }25152516            _ => T::AccountId::default(),2517        };25182519        // Sponsor smart contracts2520        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {25212522            // On instantiation: set the contract owner2523            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {25242525                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2526                    &who,2527                    code_hash,2528                    salt,2529                );2530                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());25312532                T::AccountId::default()2533            },25342535            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2536            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {25372538                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());25392540                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2541                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2542                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2543                  2544                if !owned_contract && white_list_enabled {2545                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2546                        return Err(InvalidTransaction::Call.into());2547                    }2548                }25492550                let mut sponsor_transfer = false;2551                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2552                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2553                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2554                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2555                    let limit_time = last_tx_block + rate_limit;25562557                    if block_number >= limit_time {2558                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2559                        sponsor_transfer = true;2560                    }2561                } else {2562                    sponsor_transfer = false;2563                }2564               2565                2566                let mut sp = T::AccountId::default();2567                if sponsor_transfer {2568                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2569                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2570                            sp = called_contract;2571                        }2572                    }2573                }25742575                sp2576            },25772578            _ => sponsor,2579        };25802581        let mut who_pays_fee: T::AccountId = sponsor.clone();2582        if sponsor == T::AccountId::default() {2583            who_pays_fee = who.clone();2584        }25852586		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2587			.map(|i| (fee, i))2588    }2589}259025912592impl<T: Config + Send + Sync> SignedExtension2593    for ChargeTransactionPayment<T>2594where2595    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2596    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2597    T::AccountId: AsRef<[u8]>,2598    T::AccountId: UncheckedFrom<T::Hash>,2599{2600    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2601    type AccountId = T::AccountId;2602    type Call = T::Call;2603    type AdditionalSigned = ();2604    type Pre = (2605        // tip2606        BalanceOf<T>,2607        // who pays fee2608        Self::AccountId,2609		// imbalance resulting from withdrawing the fee2610		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2611    );2612    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2613        Ok(())2614    }26152616    fn validate(2617        &self,2618        who: &Self::AccountId,2619        call: &Self::Call,2620        info: &DispatchInfoOf<Self::Call>,2621        len: usize,2622    ) -> TransactionValidity {2623		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2624		Ok(ValidTransaction {2625			priority: Self::get_priority(len, info, fee),2626			..Default::default()2627		})2628    }26292630    fn pre_dispatch(2631        self,2632        who: &Self::AccountId,2633        call: &Self::Call,2634        info: &DispatchInfoOf<Self::Call>,2635        len: usize,2636    ) -> Result<Self::Pre, TransactionValidityError> {2637        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2638        Ok((self.0, who.clone(), imbalance))2639    }26402641    fn post_dispatch(2642        pre: Self::Pre,2643        info: &DispatchInfoOf<Self::Call>,2644        post_info: &PostDispatchInfoOf<Self::Call>,2645        len: usize,2646        _result: &DispatchResult,2647    ) -> Result<(), TransactionValidityError> {2648		let (tip, who, imbalance) = pre;2649		let actual_fee = transaction_payment::Module::<T>::compute_actual_fee(2650			len as u32,2651			info,2652			post_info,2653			tip,2654		);2655		<T as transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2656		Ok(())2657    }2658}26592660// #endregion
after · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, IsSubType,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial, DispatchClass,29    },30    StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use sp_runtime::traits::StaticLookup;45use pallet_contracts::chain_extension::UncheckedFrom;46use transaction_payment::OnChargeTransaction;4748#[cfg(test)]49mod mock;5051#[cfg(test)]52mod tests;5354mod default_weights;5556pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;57pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;58pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;59pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6061// Structs62// #region6364pub type CollectionId = u32;65pub type TokenId = u32;66pub type DecimalPoints = u8;6768#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]69#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]70pub enum CollectionMode {71    Invalid,72    NFT,73    // decimal points74    Fungible(DecimalPoints),75    ReFungible,76}7778impl Default for CollectionMode {79    fn default() -> Self {80        Self::Invalid81    }82}8384impl Into<u8> for CollectionMode {85    fn into(self) -> u8 {86        match self {87            CollectionMode::Invalid => 0,88            CollectionMode::NFT => 1,89            CollectionMode::Fungible(_) => 2,90            CollectionMode::ReFungible => 3,91        }92    }93}9495#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]96#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]97pub enum AccessMode {98    Normal,99    WhiteList,100}101impl Default for AccessMode {102    fn default() -> Self {103        Self::Normal104    }105}106107#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]108#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]109pub enum SchemaVersion {110    ImageURL,111    Unique,112}113impl Default for SchemaVersion {114    fn default() -> Self {115        Self::ImageURL116    }117}118119#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]120#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]121pub struct Ownership<AccountId> {122    pub owner: AccountId,123    pub fraction: u128,124}125126#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub struct CollectionType<AccountId> {129    pub owner: AccountId,130    pub mode: CollectionMode,131    pub access: AccessMode,132    pub decimal_points: DecimalPoints,133    pub name: Vec<u16>,        // 64 include null escape char134    pub description: Vec<u16>, // 256 include null escape char135    pub token_prefix: Vec<u8>, // 16 include null escape char136    pub mint_mode: bool,137    pub offchain_schema: Vec<u8>,138    pub schema_version: SchemaVersion,139    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender140    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.141    pub limits: CollectionLimits, // Collection private restrictions 142    pub variable_on_chain_schema: Vec<u8>, //143    pub const_on_chain_schema: Vec<u8>, //144}145146#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]147#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]148pub struct NftItemType<AccountId> {149    pub owner: AccountId,150    pub const_data: Vec<u8>,151    pub variable_data: Vec<u8>,152}153154#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]155#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]156pub struct FungibleItemType {157    pub value: u128,158}159160#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]161#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]162pub struct ReFungibleItemType<AccountId> {163    pub owner: Vec<Ownership<AccountId>>,164    pub const_data: Vec<u8>,165    pub variable_data: Vec<u8>,166}167168// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]169// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170// pub struct VestingItem<AccountId, Moment> {171//     pub sender: AccountId,172//     pub recipient: AccountId,173//     pub collection_id: CollectionId,174//     pub item_id: TokenId,175//     pub amount: u64,176//     pub vesting_date: Moment,177// }178179#[derive(Encode, Decode, Debug, Clone, PartialEq)]180#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]181pub struct CollectionLimits {182    pub account_token_ownership_limit: u32,183    pub sponsored_data_size: u32,184    pub token_limit: u32,185186    // Timeouts for item types in passed blocks187    pub sponsor_transfer_timeout: u32,188    pub owner_can_transfer: bool,189    pub owner_can_destroy: bool,190}191192impl Default for CollectionLimits {193    fn default() -> CollectionLimits {194        CollectionLimits { 195            account_token_ownership_limit: 10_000_000, 196            token_limit: u32::max_value(),197            sponsored_data_size: u32::max_value(), 198            sponsor_transfer_timeout: 14400,199            owner_can_transfer: true,200            owner_can_destroy: true201        }202    }203}204205#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]206#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]207pub struct ChainLimits {208    pub collection_numbers_limit: u32,209    pub account_token_ownership_limit: u32,210    pub collections_admins_limit: u64,211    pub custom_data_limit: u32,212213    // Timeouts for item types in passed blocks214    pub nft_sponsor_transfer_timeout: u32,215    pub fungible_sponsor_transfer_timeout: u32,216    pub refungible_sponsor_transfer_timeout: u32,217218    // Schema limits219    pub offchain_schema_limit: u32,220    pub variable_on_chain_schema_limit: u32,221    pub const_on_chain_schema_limit: u32,222}223224pub trait WeightInfo {225	fn create_collection() -> Weight;226	fn destroy_collection() -> Weight;227	fn add_to_white_list() -> Weight;228	fn remove_from_white_list() -> Weight;229    fn set_public_access_mode() -> Weight;230    fn set_mint_permission() -> Weight;231    fn change_collection_owner() -> Weight;232    fn add_collection_admin() -> Weight;233    fn remove_collection_admin() -> Weight;234    fn set_collection_sponsor() -> Weight;235    fn confirm_sponsorship() -> Weight;236    fn remove_collection_sponsor() -> Weight;237    fn create_item(s: usize) -> Weight;238    fn burn_item() -> Weight;239    fn transfer() -> Weight;240    fn approve() -> Weight;241    fn transfer_from() -> Weight;242    fn set_offchain_schema() -> Weight;243    fn set_const_on_chain_schema() -> Weight;244    fn set_variable_on_chain_schema() -> Weight;245    fn set_variable_meta_data() -> Weight;246    fn enable_contract_sponsoring() -> Weight;247    fn set_schema_version() -> Weight;248    fn set_chain_limits() -> Weight;249    fn set_contract_sponsoring_rate_limit() -> Weight;250    fn toggle_contract_white_list() -> Weight;251    fn add_to_contract_white_list() -> Weight;252    fn remove_from_contract_white_list() -> Weight;253    fn set_collection_limits() -> Weight;254}255256#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]257#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]258pub struct CreateNftData {259    pub const_data: Vec<u8>,260    pub variable_data: Vec<u8>,261}262263#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]264#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]265pub struct CreateFungibleData {266    pub value: u128,267}268269#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]270#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]271pub struct CreateReFungibleData {272    pub const_data: Vec<u8>,273    pub variable_data: Vec<u8>,274    pub pieces: u128,275}276277#[derive(Encode, Decode, Debug, Clone, PartialEq)]278#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]279pub enum CreateItemData {280    NFT(CreateNftData),281    Fungible(CreateFungibleData),282    ReFungible(CreateReFungibleData),283}284285impl CreateItemData {286    pub fn len(&self) -> usize {287        let len = match self {288            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),289            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),290            _ => 0291        };292        293        return len;294    }295}296297impl From<CreateNftData> for CreateItemData {298    fn from(item: CreateNftData) -> Self {299        CreateItemData::NFT(item)300    }301}302303impl From<CreateReFungibleData> for CreateItemData {304    fn from(item: CreateReFungibleData) -> Self {305        CreateItemData::ReFungible(item)306    }307}308309impl From<CreateFungibleData> for CreateItemData {310    fn from(item: CreateFungibleData) -> Self {311        CreateItemData::Fungible(item)312    }313}314315316decl_error! {317	/// Error for non-fungible-token module.318	pub enum Error for Module<T: Config> {319        /// Total collections bound exceeded.320        TotalCollectionsLimitExceeded,321		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.322        CollectionDecimalPointLimitExceeded, 323        /// Collection name can not be longer than 63 char.324        CollectionNameLimitExceeded, 325        /// Collection description can not be longer than 255 char.326        CollectionDescriptionLimitExceeded, 327        /// Token prefix can not be longer than 15 char.328        CollectionTokenPrefixLimitExceeded,329        /// This collection does not exist.330        CollectionNotFound,331        /// Item not exists.332        TokenNotFound,333        /// Admin not found334        AdminNotFound,335        /// Arithmetic calculation overflow.336        NumOverflow,       337        /// Account already has admin role.338        AlreadyAdmin,  339        /// You do not own this collection.340        NoPermission,341        /// This address is not set as sponsor, use setCollectionSponsor first.342        ConfirmUnsetSponsorFail,343        /// Collection is not in mint mode.344        PublicMintingNotAllowed,345        /// Sender parameter and item owner must be equal.346        MustBeTokenOwner,347        /// Item balance not enough.348        TokenValueTooLow,349        /// Size of item is too large.350        NftSizeLimitExceeded,351        /// No approve found352        ApproveNotFound,353        /// Requested value more than approved.354        TokenValueNotEnough,355        /// Only approved addresses can call this method.356        ApproveRequired,357        /// Address is not in white list.358        AddresNotInWhiteList,359        /// Number of collection admins bound exceeded.360        CollectionAdminsLimitExceeded,361        /// Owned tokens by a single address bound exceeded.362        AddressOwnershipLimitExceeded,363        /// Length of items properties must be greater than 0.364        EmptyArgument,365        /// const_data exceeded data limit.366        TokenConstDataLimitExceeded,367        /// variable_data exceeded data limit.368        TokenVariableDataLimitExceeded,369        /// Not NFT item data used to mint in NFT collection.370        NotNftDataUsedToMintNftCollectionToken,371        /// Not Fungible item data used to mint in Fungible collection.372        NotFungibleDataUsedToMintFungibleCollectionToken,373        /// Not Re Fungible item data used to mint in Re Fungible collection.374        NotReFungibleDataUsedToMintReFungibleCollectionToken,375        /// Unexpected collection type.376        UnexpectedCollectionType,377        /// Can't store metadata in fungible tokens.378        CantStoreMetadataInFungibleTokens,379        /// Collection token limit exceeded380        CollectionTokenLimitExceeded,381        /// Account token limit exceeded per collection382        AccountTokenLimitExceeded,383        /// Collection limit bounds per collection exceeded384        CollectionLimitBoundsExceeded,385        /// Schema data size limit bound exceeded386        SchemaDataLimitExceeded,387        /// Maximum refungibility exceeded388        WrongRefungiblePieces389	}390}391392pub trait Config: system::Config + Sized + transaction_payment::Config + pallet_contracts::Config {393    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;394395    /// Weight information for extrinsics in this pallet.396	type WeightInfo: WeightInfo;397}398399#[cfg(feature = "runtime-benchmarks")]400mod benchmarking;401402// #endregion403404decl_storage! {405    trait Store for Module<T: Config> as Nft {406407        // Private members408        NextCollectionID: CollectionId;409        CreatedCollectionCount: u32;410        ChainVersion: u64;411        ItemListIndex: map hasher(identity) CollectionId => TokenId;412413        // Chain limits struct414        pub ChainLimit get(fn chain_limit) config(): ChainLimits;415416        // Bound counters417        CollectionCount: u32;418        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;419420        // Basic collections421        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;422        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;423        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;424425        /// Balance owner per collection map426        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;427428        /// second parameter: item id + owner account id + spender account id429        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;430431        /// Item collections432        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;433        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;434        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;435436        /// Index list437        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;438439        /// Tokens transfer baskets440        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;441        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;442        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;443        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;444445        // Contract Sponsorship and Ownership446        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;447        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;448        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;449        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;450        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 451        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 452    }453    add_extra_genesis {454        build(|config: &GenesisConfig<T>| {455            // Modification of storage456            for (_num, _c) in &config.collection {457                <Module<T>>::init_collection(_c);458            }459460            for (_num, _c, _i) in &config.nft_item_id {461                <Module<T>>::init_nft_token(*_c, _i);462            }463464            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {465                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);466            }467468            for (_num, _c, _i) in &config.refungible_item_id {469                <Module<T>>::init_refungible_token(*_c, _i);470            }471        })472    }473}474475decl_event!(476    pub enum Event<T>477    where478        AccountId = <T as system::Config>::AccountId,479    {480        /// New collection was created481        /// 482        /// # Arguments483        /// 484        /// * collection_id: Globally unique identifier of newly created collection.485        /// 486        /// * mode: [CollectionMode] converted into u8.487        /// 488        /// * account_id: Collection owner.489        Created(CollectionId, u8, AccountId),490491        /// New item was created.492        /// 493        /// # Arguments494        /// 495        /// * collection_id: Id of the collection where item was created.496        /// 497        /// * item_id: Id of an item. Unique within the collection.498        ItemCreated(CollectionId, TokenId),499500        /// Collection item was burned.501        /// 502        /// # Arguments503        /// 504        /// collection_id.505        /// 506        /// item_id: Identifier of burned NFT.507        ItemDestroyed(CollectionId, TokenId),508    }509);510511decl_module! {512    pub struct Module<T: Config> for enum Call 513    where 514        origin: T::Origin515    {516        fn deposit_event() = default;517        type Error = Error<T>;518519        fn on_initialize(now: T::BlockNumber) -> Weight {520521            if ChainVersion::get() < 2522            {523                let value = NextCollectionID::get();524                CreatedCollectionCount::put(value);525                ChainVersion::put(2);526            }527528            0529        }530531        /// 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.532        /// 533        /// # Permissions534        /// 535        /// * Anyone.536        /// 537        /// # Arguments538        /// 539        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.540        /// 541        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.542        /// 543        /// * token_prefix: UTF-8 string with token prefix.544        /// 545        /// * mode: [CollectionMode] collection type and type dependent data.546        // returns collection ID547        #[weight = <T as Config>::WeightInfo::create_collection()]548        pub fn create_collection(origin,549                                 collection_name: Vec<u16>,550                                 collection_description: Vec<u16>,551                                 token_prefix: Vec<u8>,552                                 mode: CollectionMode) -> DispatchResult {553554            // Anyone can create a collection555            let who = ensure_signed(origin)?;556557            let decimal_points = match mode {558                CollectionMode::Fungible(points) => points,559                _ => 0560            };561562            // bound Total number of collections563            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);564565            // check params566            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);567            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);568            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);569            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);570571            // Generate next collection ID572            let next_id = CreatedCollectionCount::get()573                .checked_add(1)574                .ok_or(Error::<T>::NumOverflow)?;575576            // bound counter577            let total = CollectionCount::get()578                .checked_add(1)579                .ok_or(Error::<T>::NumOverflow)?;580581            CreatedCollectionCount::put(next_id);582            CollectionCount::put(total);583584            // Create new collection585            let new_collection = CollectionType {586                owner: who.clone(),587                name: collection_name,588                mode: mode.clone(),589                mint_mode: false,590                access: AccessMode::Normal,591                description: collection_description,592                decimal_points: decimal_points,593                token_prefix: token_prefix,594                offchain_schema: Vec::new(),595                schema_version: SchemaVersion::ImageURL,596                sponsor: T::AccountId::default(),597                sponsor_confirmed: false,598                variable_on_chain_schema: Vec::new(),599                const_on_chain_schema: Vec::new(),600                limits: CollectionLimits::default()601            };602603            // Add new collection to map604            <Collection<T>>::insert(next_id, new_collection);605606            // call event607            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));608609            Ok(())610        }611612        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.613        /// 614        /// # Permissions615        /// 616        /// * Collection Owner.617        /// 618        /// # Arguments619        /// 620        /// * collection_id: collection to destroy.621        #[weight = <T as Config>::WeightInfo::destroy_collection()]622        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {623624            let sender = ensure_signed(origin)?;625            Self::check_owner_permissions(collection_id, sender)?;626627            <AddressTokens<T>>::remove_prefix(collection_id);628            <Allowances<T>>::remove_prefix(collection_id);629            <Balance<T>>::remove_prefix(collection_id);630            <ItemListIndex>::remove(collection_id);631            <AdminList<T>>::remove(collection_id);632            <Collection<T>>::remove(collection_id);633            <WhiteList<T>>::remove_prefix(collection_id);634635            <NftItemList<T>>::remove_prefix(collection_id);636            <FungibleItemList<T>>::remove_prefix(collection_id);637            <ReFungibleItemList<T>>::remove_prefix(collection_id);638639            <NftTransferBasket<T>>::remove_prefix(collection_id);640            <FungibleTransferBasket<T>>::remove_prefix(collection_id);641            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);642643            if CollectionCount::get() > 0644            {645                // bound couter646                let total = CollectionCount::get()647                    .checked_sub(1)648                    .ok_or(Error::<T>::NumOverflow)?;649650                CollectionCount::put(total);651            }652653            Ok(())654        }655656        /// Add an address to white list.657        /// 658        /// # Permissions659        /// 660        /// * Collection Owner661        /// * Collection Admin662        /// 663        /// # Arguments664        /// 665        /// * collection_id.666        /// 667        /// * address.668        #[weight = <T as Config>::WeightInfo::add_to_white_list()]669        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{670671            let sender = ensure_signed(origin)?;672            Self::check_owner_or_admin_permissions(collection_id, sender)?;673674            <WhiteList<T>>::insert(collection_id, address, true);675            676            Ok(())677        }678679        /// Remove an address from white list.680        /// 681        /// # Permissions682        /// 683        /// * Collection Owner684        /// * Collection Admin685        /// 686        /// # Arguments687        /// 688        /// * collection_id.689        /// 690        /// * address.691        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]692        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{693694            let sender = ensure_signed(origin)?;695            Self::check_owner_or_admin_permissions(collection_id, sender)?;696697            <WhiteList<T>>::remove(collection_id, address);698699            Ok(())700        }701702        /// Toggle between normal and white list access for the methods with access for `Anyone`.703        /// 704        /// # Permissions705        /// 706        /// * Collection Owner.707        /// 708        /// # Arguments709        /// 710        /// * collection_id.711        /// 712        /// * mode: [AccessMode]713        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]714        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult715        {716            let sender = ensure_signed(origin)?;717718            Self::check_owner_permissions(collection_id, sender)?;719            let mut target_collection = <Collection<T>>::get(collection_id);720            target_collection.access = mode;721            <Collection<T>>::insert(collection_id, target_collection);722723            Ok(())724        }725726        /// Allows Anyone to create tokens if:727        /// * White List is enabled, and728        /// * Address is added to white list, and729        /// * This method was called with True parameter730        /// 731        /// # Permissions732        /// * Collection Owner733        ///734        /// # Arguments735        /// 736        /// * collection_id.737        /// 738        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.739        #[weight = <T as Config>::WeightInfo::set_mint_permission()]740        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult741        {742            let sender = ensure_signed(origin)?;743744            Self::check_owner_permissions(collection_id, sender)?;745            let mut target_collection = <Collection<T>>::get(collection_id);746            target_collection.mint_mode = mint_permission;747            <Collection<T>>::insert(collection_id, target_collection);748749            Ok(())750        }751752        /// Change the owner of the collection.753        /// 754        /// # Permissions755        /// 756        /// * Collection Owner.757        /// 758        /// # Arguments759        /// 760        /// * collection_id.761        /// 762        /// * new_owner.763        #[weight = <T as Config>::WeightInfo::change_collection_owner()]764        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {765766            let sender = ensure_signed(origin)?;767            Self::check_owner_permissions(collection_id, sender)?;768            let mut target_collection = <Collection<T>>::get(collection_id);769            target_collection.owner = new_owner;770            <Collection<T>>::insert(collection_id, target_collection);771772            Ok(())773        }774775        /// Adds an admin of the Collection.776        /// 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. 777        /// 778        /// # Permissions779        /// 780        /// * Collection Owner.781        /// * Collection Admin.782        /// 783        /// # Arguments784        /// 785        /// * collection_id: ID of the Collection to add admin for.786        /// 787        /// * new_admin_id: Address of new admin to add.788        #[weight = <T as Config>::WeightInfo::add_collection_admin()]789        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {790791            let sender = ensure_signed(origin)?;792            Self::check_owner_or_admin_permissions(collection_id, sender)?;793            let mut admin_arr: Vec<T::AccountId> = Vec::new();794795            if <AdminList<T>>::contains_key(collection_id)796            {797                admin_arr = <AdminList<T>>::get(collection_id);798                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);799            }800801            // Number of collection admins802            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);803804            admin_arr.push(new_admin_id);805            <AdminList<T>>::insert(collection_id, admin_arr);806807            Ok(())808        }809810        /// 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.811        ///812        /// # Permissions813        /// 814        /// * Collection Owner.815        /// * Collection Admin.816        /// 817        /// # Arguments818        /// 819        /// * collection_id: ID of the Collection to remove admin for.820        /// 821        /// * account_id: Address of admin to remove.822        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]823        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {824825            let sender = ensure_signed(origin)?;826            Self::check_owner_or_admin_permissions(collection_id, sender)?;827            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);828829            let mut admin_arr = <AdminList<T>>::get(collection_id);830            admin_arr.retain(|i| *i != account_id);831            <AdminList<T>>::insert(collection_id, admin_arr);832833            Ok(())834        }835836        /// # Permissions837        /// 838        /// * Collection Owner839        /// 840        /// # Arguments841        /// 842        /// * collection_id.843        /// 844        /// * new_sponsor.845        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]846        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {847848            let sender = ensure_signed(origin)?;849            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);850851            let mut target_collection = <Collection<T>>::get(collection_id);852            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);853854            target_collection.sponsor = new_sponsor;855            target_collection.sponsor_confirmed = false;856            <Collection<T>>::insert(collection_id, target_collection);857858            Ok(())859        }860861        /// # Permissions862        /// 863        /// * Sponsor.864        /// 865        /// # Arguments866        /// 867        /// * collection_id.868        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]869        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {870871            let sender = ensure_signed(origin)?;872            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);873874            let mut target_collection = <Collection<T>>::get(collection_id);875            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);876877            target_collection.sponsor_confirmed = true;878            <Collection<T>>::insert(collection_id, target_collection);879880            Ok(())881        }882883        /// Switch back to pay-per-own-transaction model.884        ///885        /// # Permissions886        ///887        /// * Collection owner.888        /// 889        /// # Arguments890        /// 891        /// * collection_id.892        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]893        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {894895            let sender = ensure_signed(origin)?;896            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);897898            let mut target_collection = <Collection<T>>::get(collection_id);899            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);900901            target_collection.sponsor = T::AccountId::default();902            target_collection.sponsor_confirmed = false;903            <Collection<T>>::insert(collection_id, target_collection);904905            Ok(())906        }907908        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.909        /// 910        /// # Permissions911        /// 912        /// * Collection Owner.913        /// * Collection Admin.914        /// * Anyone if915        ///     * White List is enabled, and916        ///     * Address is added to white list, and917        ///     * MintPermission is enabled (see SetMintPermission method)918        /// 919        /// # Arguments920        /// 921        /// * collection_id: ID of the collection.922        /// 923        /// * owner: Address, initial owner of the NFT.924        ///925        /// * data: Token data to store on chain.926        // #[weight =927        // (130_000_000 as Weight)928        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))929        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))930        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]931932        #[weight = <T as Config>::WeightInfo::create_item(data.len())]933        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {934935            let sender = ensure_signed(origin)?;936937            Self::collection_exists(collection_id)?;938939            let target_collection = <Collection<T>>::get(collection_id);940941            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;942            Self::validate_create_item_args(&target_collection, &data)?;943            Self::create_item_no_validation(collection_id, owner, data)?;944945            Ok(())946        }947948        /// This method creates multiple instances of NFT Collection created with CreateCollection method.949        /// 950        /// # Permissions951        /// 952        /// * Collection Owner.953        /// * Collection Admin.954        /// * Anyone if955        ///     * White List is enabled, and956        ///     * Address is added to white list, and957        ///     * MintPermission is enabled (see SetMintPermission method)958        /// 959        /// # Arguments960        /// 961        /// * collection_id: ID of the collection.962        /// 963        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].964        /// 965        /// * owner: Address, initial owner of the NFT.966        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()967                               .map(|data| { data.len() })968                               .sum())]969        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {970971            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);972            let sender = ensure_signed(origin)?;973974            Self::collection_exists(collection_id)?;975            let target_collection = <Collection<T>>::get(collection_id);976977            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;978979            for data in &items_data {980                Self::validate_create_item_args(&target_collection, data)?;981            }982            for data in &items_data {983                Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?;984            }985986            Ok(())987        }988989        /// Destroys a concrete instance of NFT.990        /// 991        /// # Permissions992        /// 993        /// * Collection Owner.994        /// * Collection Admin.995        /// * Current NFT Owner.996        /// 997        /// # Arguments998        /// 999        /// * collection_id: ID of the collection.1000        /// 1001        /// * item_id: ID of NFT to burn.1002        #[weight = <T as Config>::WeightInfo::burn_item()]1003        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10041005            let sender = ensure_signed(origin)?;1006            Self::collection_exists(collection_id)?;10071008            // Transfer permissions check1009            let target_collection = <Collection<T>>::get(collection_id);1010            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1011                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1012                Error::<T>::NoPermission);10131014            if target_collection.access == AccessMode::WhiteList {1015                Self::check_white_list(collection_id, &sender)?;1016            }10171018            match target_collection.mode1019            {1020                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1021                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1022                CollectionMode::ReFungible  => Self::burn_refungible_item(collection_id, item_id, &sender)?,1023                _ => ()1024            };10251026            // call event1027            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10281029            Ok(())1030        }10311032        /// Change ownership of the token.1033        /// 1034        /// # Permissions1035        /// 1036        /// * Collection Owner1037        /// * Collection Admin1038        /// * Current NFT owner1039        ///1040        /// # Arguments1041        /// 1042        /// * recipient: Address of token recipient.1043        /// 1044        /// * collection_id.1045        /// 1046        /// * item_id: ID of the item1047        ///     * Non-Fungible Mode: Required.1048        ///     * Fungible Mode: Ignored.1049        ///     * Re-Fungible Mode: Required.1050        /// 1051        /// * value: Amount to transfer.1052        ///     * Non-Fungible Mode: Ignored1053        ///     * Fungible Mode: Must specify transferred amount1054        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1055        #[weight = <T as Config>::WeightInfo::transfer()]1056        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1057            let sender = ensure_signed(origin)?;1058            Self::transfer_internal(sender, recipient, collection_id, item_id, value)1059        }10601061        /// Set, change, or remove approved address to transfer the ownership of the NFT.1062        /// 1063        /// # Permissions1064        /// 1065        /// * Collection Owner1066        /// * Collection Admin1067        /// * Current NFT owner1068        /// 1069        /// # Arguments1070        /// 1071        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1072        /// 1073        /// * collection_id.1074        /// 1075        /// * item_id: ID of the item.1076        #[weight = <T as Config>::WeightInfo::approve()]1077        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {10781079            let sender = ensure_signed(origin)?;10801081            Self::collection_exists(collection_id)?;1082            Self::token_exists(collection_id, item_id, &sender)?;10831084            // Transfer permissions check1085            let target_collection = <Collection<T>>::get(collection_id);1086            let allowance_limit = if Self::is_owner_or_admin_permissions(1087                collection_id,1088                sender.clone(),1089            ) {1090                None1091            } else if let Some(amount) = Self::owned_amount(1092                sender.clone(),1093                collection_id,1094                item_id,1095            ) {1096                Some(amount)1097            } else {1098                fail!(Error::<T>::NoPermission);1099            };11001101            if target_collection.access == AccessMode::WhiteList {1102                Self::check_white_list(collection_id, &sender)?;1103                Self::check_white_list(collection_id, &spender)?;1104            }11051106            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1107            let mut allowance: u128 = amount;1108            if allowance_exists {1109                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1110            }1111            if let Some(limit) = allowance_limit {1112                ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1113            }1114            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11151116            Ok(())1117        }1118        1119        /// 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.1120        /// 1121        /// # Permissions1122        /// * Collection Owner1123        /// * Collection Admin1124        /// * Current NFT owner1125        /// * Address approved by current NFT owner1126        /// 1127        /// # Arguments1128        /// 1129        /// * from: Address that owns token.1130        /// 1131        /// * recipient: Address of token recipient.1132        /// 1133        /// * collection_id.1134        /// 1135        /// * item_id: ID of the item.1136        /// 1137        /// * value: Amount to transfer.1138        #[weight = <T as Config>::WeightInfo::transfer_from()]1139        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11401141            let sender = ensure_signed(origin)?;1142            let mut appoved_transfer = false;11431144            // Check approval1145            let mut approval: u128 = 0;1146            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1147                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1148                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1149                appoved_transfer = true;1150            }11511152            let target_collection = <Collection<T>>::get(collection_id);11531154            // Limits check1155            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11561157            // Transfer permissions check         1158            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1159                Error::<T>::NoPermission);11601161            if target_collection.access == AccessMode::WhiteList {1162                Self::check_white_list(collection_id, &sender)?;1163                Self::check_white_list(collection_id, &recipient)?;1164            }11651166            // Reduce approval by transferred amount or remove if remaining approval drops to 01167            if approval.checked_sub(value).unwrap_or(0) > 0 {1168                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1169            }1170            else {1171                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1172            }11731174            match target_collection.mode1175            {1176                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1177                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1178                CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1179                _ => ()1180            };11811182            Ok(())1183        }11841185        // #[weight = 0]1186        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11871188        //     // let no_perm_mes = "You do not have permissions to modify this collection";1189        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1190        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1191        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11921193        //     // // on_nft_received  call11941195        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;11961197        //     Ok(())1198        // }11991200        /// Set off-chain data schema.1201        /// 1202        /// # Permissions1203        /// 1204        /// * Collection Owner1205        /// * Collection Admin1206        /// 1207        /// # Arguments1208        /// 1209        /// * collection_id.1210        /// 1211        /// * schema: String representing the offchain data schema.1212        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1213        pub fn set_variable_meta_data (1214            origin,1215            collection_id: CollectionId,1216            item_id: TokenId,1217            data: Vec<u8>1218        ) -> DispatchResult {1219            let sender = ensure_signed(origin)?;1220            1221            Self::collection_exists(collection_id)?;1222            Self::token_exists(collection_id, item_id, &sender)?;12231224            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12251226            // Modify permissions check1227            let target_collection = <Collection<T>>::get(collection_id);1228            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1229                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1230                Error::<T>::NoPermission);12311232            match target_collection.mode1233            {1234                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1235                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1236                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1237                _ => fail!(Error::<T>::UnexpectedCollectionType)1238            };12391240            Ok(())1241        }1242 1243        /// Set schema standard1244        /// ImageURL1245        /// Unique1246        /// 1247        /// # Permissions1248        /// 1249        /// * Collection Owner1250        /// * Collection Admin1251        /// 1252        /// # Arguments1253        /// 1254        /// * collection_id.1255        /// 1256        /// * schema: SchemaVersion: enum1257        #[weight = <T as Config>::WeightInfo::set_schema_version()]1258        pub fn set_schema_version(1259            origin,1260            collection_id: CollectionId,1261            version: SchemaVersion1262        ) -> DispatchResult {1263            let sender = ensure_signed(origin)?;1264            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1265            let mut target_collection = <Collection<T>>::get(collection_id);1266            target_collection.schema_version = version;1267            <Collection<T>>::insert(collection_id, target_collection);12681269            Ok(())1270        }12711272        /// Set off-chain data schema.1273        /// 1274        /// # Permissions1275        /// 1276        /// * Collection Owner1277        /// * Collection Admin1278        /// 1279        /// # Arguments1280        /// 1281        /// * collection_id.1282        /// 1283        /// * schema: String representing the offchain data schema.1284        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1285        pub fn set_offchain_schema(1286            origin,1287            collection_id: CollectionId,1288            schema: Vec<u8>1289        ) -> DispatchResult {1290            let sender = ensure_signed(origin)?;1291            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12921293            // check schema limit1294            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");12951296            let mut target_collection = <Collection<T>>::get(collection_id);1297            target_collection.offchain_schema = schema;1298            <Collection<T>>::insert(collection_id, target_collection);12991300            Ok(())1301        }13021303        /// Set const on-chain data schema.1304        /// 1305        /// # Permissions1306        /// 1307        /// * Collection Owner1308        /// * Collection Admin1309        /// 1310        /// # Arguments1311        /// 1312        /// * collection_id.1313        /// 1314        /// * schema: String representing the const on-chain data schema.1315        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1316        pub fn set_const_on_chain_schema (1317            origin,1318            collection_id: CollectionId,1319            schema: Vec<u8>1320        ) -> DispatchResult {1321            let sender = ensure_signed(origin)?;1322            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13231324            // check schema limit1325            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");13261327            let mut target_collection = <Collection<T>>::get(collection_id);1328            target_collection.const_on_chain_schema = schema;1329            <Collection<T>>::insert(collection_id, target_collection);13301331            Ok(())1332        }13331334        /// Set variable on-chain data schema.1335        /// 1336        /// # Permissions1337        /// 1338        /// * Collection Owner1339        /// * Collection Admin1340        /// 1341        /// # Arguments1342        /// 1343        /// * collection_id.1344        /// 1345        /// * schema: String representing the variable on-chain data schema.1346        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1347        pub fn set_variable_on_chain_schema (1348            origin,1349            collection_id: CollectionId,1350            schema: Vec<u8>1351        ) -> DispatchResult {1352            let sender = ensure_signed(origin)?;1353            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13541355            // check schema limit1356            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");13571358            let mut target_collection = <Collection<T>>::get(collection_id);1359            target_collection.variable_on_chain_schema = schema;1360            <Collection<T>>::insert(collection_id, target_collection);13611362            Ok(())1363        }13641365        // Sudo permissions function1366        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1367        pub fn set_chain_limits(1368            origin,1369            limits: ChainLimits1370        ) -> DispatchResult {13711372            #[cfg(not(feature = "runtime-benchmarks"))]1373            ensure_root(origin)?;13741375            <ChainLimit>::put(limits);1376            Ok(())1377        }13781379        /// Enable smart contract self-sponsoring.1380        /// 1381        /// # Permissions1382        /// 1383        /// * Contract Owner1384        /// 1385        /// # Arguments1386        /// 1387        /// * contract address1388        /// * enable flag1389        /// 1390        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1391        pub fn enable_contract_sponsoring(1392            origin,1393            contract_address: T::AccountId,1394            enable: bool1395        ) -> DispatchResult {13961397            let sender = ensure_signed(origin)?;13981399            #[cfg(feature = "runtime-benchmarks")]1400            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14011402            Self::ensure_contract_owned(sender, &contract_address)?;14031404            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1405            Ok(())1406        }14071408        /// Set the rate limit for contract sponsoring to specified number of blocks.1409        /// 1410        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1411        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1412        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1413        /// from contract endowment if there are at least B blocks between such transactions. 1414        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1415        /// 1416        /// # Permissions1417        /// 1418        /// * Contract Owner1419        /// 1420        /// # Arguments1421        /// 1422        /// -`contract_address`: Address of the contract to sponsor1423        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1424        /// 1425        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1426        pub fn set_contract_sponsoring_rate_limit(1427            origin,1428            contract_address: T::AccountId,1429            rate_limit: T::BlockNumber1430        ) -> DispatchResult {1431            let sender = ensure_signed(origin)?;14321433            #[cfg(feature = "runtime-benchmarks")]1434            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14351436            Self::ensure_contract_owned(sender, &contract_address)?;1437            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1438            Ok(())1439        }14401441        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1442        /// 1443        /// # Permissions1444        /// 1445        /// * Address that deployed smart contract.1446        /// 1447        /// # Arguments1448        /// 1449        /// -`contract_address`: Address of the contract.1450        /// 1451        /// - `enable`: .  1452        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1453        pub fn toggle_contract_white_list(1454            origin,1455            contract_address: T::AccountId,1456            enable: bool1457        ) -> DispatchResult {1458            let sender = ensure_signed(origin)?;14591460            #[cfg(feature = "runtime-benchmarks")]1461            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14621463            Self::ensure_contract_owned(sender, &contract_address)?;1464            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1465            Ok(())1466        }1467        1468        /// Add an address to smart contract white list.1469        /// 1470        /// # Permissions1471        /// 1472        /// * Address that deployed smart contract.1473        /// 1474        /// # Arguments1475        /// 1476        /// -`contract_address`: Address of the contract.1477        ///1478        /// -`account_address`: Address to add.1479        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1480        pub fn add_to_contract_white_list(1481            origin,1482            contract_address: T::AccountId,1483            account_address: T::AccountId1484        ) -> DispatchResult {1485            let sender = ensure_signed(origin)?;14861487            #[cfg(feature = "runtime-benchmarks")]1488            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1489            1490            Self::ensure_contract_owned(sender, &contract_address)?;      1491            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1492            Ok(())1493        }14941495        /// Remove an address from smart contract white list.1496        /// 1497        /// # Permissions1498        /// 1499        /// * Address that deployed smart contract.1500        /// 1501        /// # Arguments1502        /// 1503        /// -`contract_address`: Address of the contract.1504        ///1505        /// -`account_address`: Address to remove.1506        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1507        pub fn remove_from_contract_white_list(1508            origin,1509            contract_address: T::AccountId,1510            account_address: T::AccountId1511        ) -> DispatchResult {1512            let sender = ensure_signed(origin)?;15131514            #[cfg(feature = "runtime-benchmarks")]1515            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15161517            Self::ensure_contract_owned(sender, &contract_address)?;1518            <ContractWhiteList<T>>::remove(contract_address, account_address);1519            Ok(())1520        }15211522        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1523        pub fn set_collection_limits(1524            origin,1525            collection_id: u32,1526            limits: CollectionLimits,1527        ) -> DispatchResult {1528            let sender = ensure_signed(origin)?;1529            Self::check_owner_permissions(collection_id, sender.clone())?;1530            let mut target_collection = <Collection<T>>::get(collection_id);1531            let chain_limits = ChainLimit::get();1532            let climits = target_collection.limits;15331534            // collection bounds1535            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1536                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1537                Error::<T>::CollectionLimitBoundsExceeded);15381539            // token_limit   check  prev1540            ensure!(climits.token_limit > limits.token_limit && 1541                limits.token_limit <= chain_limits.account_token_ownership_limit, 1542                Error::<T>::AccountTokenLimitExceeded);15431544            target_collection.limits = limits;1545            <Collection<T>>::insert(collection_id, target_collection);15461547            Ok(())1548        } 1549    }1550}15511552impl<T: Config> Module<T> {15531554    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {15551556        let target_collection = <Collection<T>>::get(collection_id);15571558        // Limits check1559        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;15601561        // Transfer permissions check1562        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1563            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1564            Error::<T>::NoPermission);15651566        if target_collection.access == AccessMode::WhiteList {1567            Self::check_white_list(collection_id, &sender)?;1568            Self::check_white_list(collection_id, &recipient)?;1569        }15701571        match target_collection.mode1572        {1573            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1574            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1575            CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1576            _ => ()1577        };15781579        Ok(())1580    }158115821583    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15841585        // check token limit and account token limit1586        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1587        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1588        1589        Ok(())1590    }15911592    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15931594        // check token limit and account token limit1595        let total_items: u32 = ItemListIndex::get(collection_id);1596        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1597        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1598        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15991600        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1601            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1602            Self::check_white_list(collection_id, owner)?;1603            Self::check_white_list(collection_id, sender)?;1604        }16051606        Ok(())1607    }16081609    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1610        match target_collection.mode1611        {1612            CollectionMode::NFT => {1613                if let CreateItemData::NFT(data) = data {1614                    // check sizes1615                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1616                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1617                } else {1618                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1619                }1620            },1621            CollectionMode::Fungible(_) => {1622                if let CreateItemData::Fungible(_) = data {1623                } else {1624                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1625                }1626            },1627            CollectionMode::ReFungible => {1628                if let CreateItemData::ReFungible(data) = data {16291630                    // check sizes1631                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1632                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16331634                    // Check refungibility limits1635                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1636                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1637                } else {1638                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1639                }1640            },1641            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1642        };16431644        Ok(())1645    }16461647    fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1648        match data1649        {1650            CreateItemData::NFT(data) => {1651                let item = NftItemType {1652                    owner,1653                    const_data: data.const_data,1654                    variable_data: data.variable_data1655                };16561657                Self::add_nft_item(collection_id, item)?;1658            },1659            CreateItemData::Fungible(data) => {1660                Self::add_fungible_item(collection_id, &owner, data.value)?;1661            },1662            CreateItemData::ReFungible(data) => {1663                let mut owner_list = Vec::new();1664                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});16651666                let item = ReFungibleItemType {1667                    owner: owner_list,1668                    const_data: data.const_data,1669                    variable_data: data.variable_data1670                };16711672                Self::add_refungible_item(collection_id, item)?;1673            }1674        };16751676        // call event1677        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16781679        Ok(())1680    }16811682    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {16831684        // Does new owner already have an account?1685        let mut balance: u128 = 0;1686        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1687            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1688        } 16891690        // Mint 1691        let item = FungibleItemType {1692            value: balance + value1693        };1694        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16951696        // Update balance1697        let new_balance = <Balance<T>>::get(collection_id, owner)1698            .checked_add(value)1699            .ok_or(Error::<T>::NumOverflow)?;1700        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17011702        Ok(())1703    }17041705    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1706        let current_index = <ItemListIndex>::get(collection_id)1707            .checked_add(1)1708            .ok_or(Error::<T>::NumOverflow)?;1709        let itemcopy = item.clone();17101711        let value = item.owner.first().unwrap().fraction;1712        let owner = item.owner.first().unwrap().owner.clone();17131714        Self::add_token_index(collection_id, current_index, &owner)?;17151716        <ItemListIndex>::insert(collection_id, current_index);1717        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17181719        // Update balance1720        let new_balance = <Balance<T>>::get(collection_id, &owner)1721            .checked_add(value)1722            .ok_or(Error::<T>::NumOverflow)?;1723        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17241725        Ok(())1726    }17271728    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1729        let current_index = <ItemListIndex>::get(collection_id)1730            .checked_add(1)1731            .ok_or(Error::<T>::NumOverflow)?;17321733        let item_owner = item.owner.clone();1734        Self::add_token_index(collection_id, current_index, &item.owner)?;17351736        <ItemListIndex>::insert(collection_id, current_index);1737        <NftItemList<T>>::insert(collection_id, current_index, item);17381739        // Update balance1740        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1741            .checked_add(1)1742            .ok_or(Error::<T>::NumOverflow)?;1743        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17441745        Ok(())1746    }17471748    fn burn_refungible_item(1749        collection_id: CollectionId,1750        item_id: TokenId,1751        owner: &T::AccountId,1752    ) -> DispatchResult {1753        ensure!(1754            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1755            Error::<T>::TokenNotFound1756        );1757        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1758        let rft_balance = token1759            .owner1760            .iter()1761            .filter(|&i| i.owner == *owner)1762            .next()1763            .unwrap();1764        Self::remove_token_index(collection_id, item_id, owner)?;17651766        // update balance1767        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1768            .checked_sub(rft_balance.fraction)1769            .ok_or(Error::<T>::NumOverflow)?;1770        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);17711772        // Re-create owners list with sender removed1773        let index = token1774            .owner1775            .iter()1776            .position(|i| i.owner == *owner)1777            .unwrap();1778        token.owner.remove(index);1779        let owner_count = token.owner.len();17801781        // Burn the token completely if this was the last (only) owner1782        if owner_count == 0 {1783            <ReFungibleItemList<T>>::remove(collection_id, item_id);1784        }1785        else {1786            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1787        }17881789        Ok(())1790    }17911792    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1793        ensure!(1794            <NftItemList<T>>::contains_key(collection_id, item_id),1795            Error::<T>::TokenNotFound1796        );1797        let item = <NftItemList<T>>::get(collection_id, item_id);1798        Self::remove_token_index(collection_id, item_id, &item.owner)?;17991800        // update balance1801        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1802            .checked_sub(1)1803            .ok_or(Error::<T>::NumOverflow)?;1804        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1805        <NftItemList<T>>::remove(collection_id, item_id);18061807        Ok(())1808    }18091810    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1811        ensure!(1812            <FungibleItemList<T>>::contains_key(collection_id, owner),1813            Error::<T>::TokenNotFound1814        );1815        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1816        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18171818        // update balance1819        let new_balance = <Balance<T>>::get(collection_id, owner)1820            .checked_sub(value)1821            .ok_or(Error::<T>::NumOverflow)?;1822        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18231824        if balance.value - value > 0 {1825            balance.value -= value;1826            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1827        }1828        else {1829            <FungibleItemList<T>>::remove(collection_id, owner);1830        }18311832        Ok(())1833    }18341835    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1836        ensure!(1837            <Collection<T>>::contains_key(collection_id),1838            Error::<T>::CollectionNotFound1839        );1840        Ok(())1841    }18421843    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1844        Self::collection_exists(collection_id)?;18451846        let target_collection = <Collection<T>>::get(collection_id);1847        ensure!(1848            subject == target_collection.owner,1849            Error::<T>::NoPermission1850        );18511852        Ok(())1853    }18541855    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1856        let target_collection = <Collection<T>>::get(collection_id);1857        let mut result: bool = subject == target_collection.owner;1858        let exists = <AdminList<T>>::contains_key(collection_id);18591860        if !result & exists {1861            if <AdminList<T>>::get(collection_id).contains(&subject) {1862                result = true1863            }1864        }18651866        result1867    }18681869    fn check_owner_or_admin_permissions(1870        collection_id: CollectionId,1871        subject: T::AccountId,1872    ) -> DispatchResult {1873        Self::collection_exists(collection_id)?;1874        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18751876        ensure!(1877            result,1878            Error::<T>::NoPermission1879        );1880        Ok(())1881    }18821883    fn owned_amount(1884        subject: T::AccountId,1885        collection_id: CollectionId,1886        item_id: TokenId,1887    ) -> Option<u128> {1888        let target_collection = <Collection<T>>::get(collection_id);18891890        match target_collection.mode {1891            CollectionMode::NFT => {1892                if <NftItemList<T>>::get(collection_id, item_id).owner == subject {1893                    return Some(1)1894                }1895                None1896            },1897            CollectionMode::Fungible(_) => {1898                if <FungibleItemList<T>>::contains_key(collection_id, &subject) {1899                    return Some(<FungibleItemList<T>>::get(collection_id, &subject)1900                        .value);1901                }1902                None1903            },1904            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)1905                .owner1906                .iter()1907                .find(|i| i.owner == subject)1908                .map(|i| i.fraction),1909            CollectionMode::Invalid => None,1910        }1911    }19121913    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1914        let target_collection = <Collection<T>>::get(collection_id);19151916        match target_collection.mode {1917            CollectionMode::NFT => {1918                <NftItemList<T>>::get(collection_id, item_id).owner == subject1919            }1920            CollectionMode::Fungible(_) => {1921                <FungibleItemList<T>>::contains_key(collection_id, &subject)1922            }1923            CollectionMode::ReFungible => {1924                <ReFungibleItemList<T>>::get(collection_id, item_id)1925                    .owner1926                    .iter()1927                    .any(|i| i.owner == subject)1928            }1929            CollectionMode::Invalid => false,1930        }1931    }19321933    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1934        let mes = Error::<T>::AddresNotInWhiteList;1935        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);19361937        Ok(())1938    }19391940    /// Check if token exists. In case of Fungible, check if there is an entry for 1941    /// the owner in fungible balances double map1942    fn token_exists(1943        collection_id: CollectionId,1944        item_id: TokenId,1945        owner: &T::AccountId1946    ) -> DispatchResult {1947        let target_collection = <Collection<T>>::get(collection_id);1948        let exists = match target_collection.mode1949        {1950            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1951            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),1952            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1953            _ => false1954        };19551956        ensure!(exists == true, Error::<T>::TokenNotFound);1957        Ok(())1958    }19591960    fn transfer_fungible(1961        collection_id: CollectionId,1962        value: u128,1963        owner: &T::AccountId,1964        recipient: &T::AccountId,1965    ) -> DispatchResult {1966        Self::token_exists(collection_id, 0, owner)?;19671968        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1969        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19701971        // Send balance to recipient (updates balanceOf of recipient)1972        Self::add_fungible_item(collection_id, recipient, value)?;19731974        // update balanceOf of sender1975        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);19761977        // Reduce or remove sender1978        if balance.value == value {1979            <FungibleItemList<T>>::remove(collection_id, owner);1980        }1981        else {1982            balance.value -= value;1983            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1984        }19851986        Ok(())1987    }19881989    fn transfer_refungible(1990        collection_id: CollectionId,1991        item_id: TokenId,1992        value: u128,1993        owner: T::AccountId,1994        new_owner: T::AccountId,1995    ) -> DispatchResult {1996        Self::token_exists(collection_id, item_id, &owner)?;19971998        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1999        let item = full_item2000            .owner2001            .iter()2002            .filter(|i| i.owner == owner)2003            .next()2004            .ok_or(Error::<T>::NumOverflow)?;2005        let amount = item.fraction;20062007        ensure!(amount >= value, Error::<T>::TokenValueTooLow);20082009        // update balance2010        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2011            .checked_sub(value)2012            .ok_or(Error::<T>::NumOverflow)?;2013        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20142015        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2016            .checked_add(value)2017            .ok_or(Error::<T>::NumOverflow)?;2018        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20192020        let old_owner = item.owner.clone();2021        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20222023        // transfer2024        if amount == value && !new_owner_has_account {2025            // change owner2026            // new owner do not have account2027            let mut new_full_item = full_item.clone();2028            new_full_item2029                .owner2030                .iter_mut()2031                .find(|i| i.owner == owner)2032                .unwrap()2033                .owner = new_owner.clone();2034            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20352036            // update index collection2037            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2038        } else {2039            let mut new_full_item = full_item.clone();2040            new_full_item2041                .owner2042                .iter_mut()2043                .find(|i| i.owner == owner)2044                .unwrap()2045                .fraction -= value;20462047            // separate amount2048            if new_owner_has_account {2049                // new owner has account2050                new_full_item2051                    .owner2052                    .iter_mut()2053                    .find(|i| i.owner == new_owner)2054                    .unwrap()2055                    .fraction += value;2056            } else {2057                // new owner do not have account2058                new_full_item.owner.push(Ownership {2059                    owner: new_owner.clone(),2060                    fraction: value,2061                });2062                Self::add_token_index(collection_id, item_id, &new_owner)?;2063            }20642065            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2066        }20672068        Ok(())2069    }20702071    fn transfer_nft(2072        collection_id: CollectionId,2073        item_id: TokenId,2074        sender: T::AccountId,2075        new_owner: T::AccountId,2076    ) -> DispatchResult {2077        Self::token_exists(collection_id, item_id, &sender)?;20782079        let mut item = <NftItemList<T>>::get(collection_id, item_id);20802081        ensure!(2082            sender == item.owner,2083            Error::<T>::MustBeTokenOwner2084        );20852086        // update balance2087        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2088            .checked_sub(1)2089            .ok_or(Error::<T>::NumOverflow)?;2090        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20912092        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2093            .checked_add(1)2094            .ok_or(Error::<T>::NumOverflow)?;2095        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20962097        // change owner2098        let old_owner = item.owner.clone();2099        item.owner = new_owner.clone();2100        <NftItemList<T>>::insert(collection_id, item_id, item);21012102        // update index collection2103        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21042105        Ok(())2106    }2107    2108    fn set_re_fungible_variable_data(2109        collection_id: CollectionId,2110        item_id: TokenId,2111        data: Vec<u8>2112    ) -> DispatchResult {2113        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);21142115        item.variable_data = data;21162117        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21182119        Ok(())2120    }21212122    fn set_nft_variable_data(2123        collection_id: CollectionId,2124        item_id: TokenId,2125        data: Vec<u8>2126    ) -> DispatchResult {2127        let mut item = <NftItemList<T>>::get(collection_id, item_id);2128        2129        item.variable_data = data;21302131        <NftItemList<T>>::insert(collection_id, item_id, item);2132        2133        Ok(())2134    }21352136    fn init_collection(item: &CollectionType<T::AccountId>) {2137        // check params2138        assert!(2139            item.decimal_points <= MAX_DECIMAL_POINTS,2140            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2141        );2142        assert!(2143            item.name.len() <= 64,2144            "Collection name can not be longer than 63 char"2145        );2146        assert!(2147            item.name.len() <= 256,2148            "Collection description can not be longer than 255 char"2149        );2150        assert!(2151            item.token_prefix.len() <= 16,2152            "Token prefix can not be longer than 15 char"2153        );21542155        // Generate next collection ID2156        let next_id = CreatedCollectionCount::get()2157            .checked_add(1)2158            .unwrap();21592160        CreatedCollectionCount::put(next_id);2161    }21622163    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2164        let current_index = <ItemListIndex>::get(collection_id)2165            .checked_add(1)2166            .unwrap();21672168        let item_owner = item.owner.clone();2169        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21702171        <ItemListIndex>::insert(collection_id, current_index);21722173        // Update balance2174        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2175            .checked_add(1)2176            .unwrap();2177        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2178    }21792180    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2181        let current_index = <ItemListIndex>::get(collection_id)2182            .checked_add(1)2183            .unwrap();21842185        Self::add_token_index(collection_id, current_index, owner).unwrap();21862187        <ItemListIndex>::insert(collection_id, current_index);21882189        // Update balance2190        let new_balance = <Balance<T>>::get(collection_id, owner)2191            .checked_add(item.value)2192            .unwrap();2193        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2194    }21952196    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2197        let current_index = <ItemListIndex>::get(collection_id)2198            .checked_add(1)2199            .unwrap();22002201        let value = item.owner.first().unwrap().fraction;2202        let owner = item.owner.first().unwrap().owner.clone();22032204        Self::add_token_index(collection_id, current_index, &owner).unwrap();22052206        <ItemListIndex>::insert(collection_id, current_index);22072208        // Update balance2209        let new_balance = <Balance<T>>::get(collection_id, &owner)2210            .checked_add(value)2211            .unwrap();2212        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2213    }22142215    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {22162217        // add to account limit2218        if <AccountItemCount<T>>::contains_key(owner) {22192220            // bound Owned tokens by a single address2221            let count = <AccountItemCount<T>>::get(owner);2222            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);22232224            <AccountItemCount<T>>::insert(owner.clone(), count2225                .checked_add(1)2226                .ok_or(Error::<T>::NumOverflow)?);2227        }2228        else {2229            <AccountItemCount<T>>::insert(owner.clone(), 1);2230        }22312232        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2233        if list_exists {2234            let mut list = <AddressTokens<T>>::get(collection_id, owner);2235            let item_contains = list.contains(&item_index.clone());22362237            if !item_contains {2238                list.push(item_index.clone());2239            }22402241            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2242        } else {2243            let mut itm = Vec::new();2244            itm.push(item_index.clone());2245            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2246        }22472248        Ok(())2249    }22502251    fn remove_token_index(2252        collection_id: CollectionId,2253        item_index: TokenId,2254        owner: &T::AccountId,2255    ) -> DispatchResult {22562257        // update counter2258        <AccountItemCount<T>>::insert(owner.clone(), 2259            <AccountItemCount<T>>::get(owner)2260            .checked_sub(1)2261            .ok_or(Error::<T>::NumOverflow)?);226222632264        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2265        if list_exists {2266            let mut list = <AddressTokens<T>>::get(collection_id, owner);2267            let item_contains = list.contains(&item_index.clone());22682269            if item_contains {2270                list.retain(|&item| item != item_index);2271                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2272            }2273        }22742275        Ok(())2276    }22772278    fn move_token_index(2279        collection_id: CollectionId,2280        item_index: TokenId,2281        old_owner: &T::AccountId,2282        new_owner: &T::AccountId,2283    ) -> DispatchResult {2284        Self::remove_token_index(collection_id, item_index, old_owner)?;2285        Self::add_token_index(collection_id, item_index, new_owner)?;22862287        Ok(())2288    }2289    2290    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2291        if <ContractOwner<T>>::contains_key(contract.clone()) {2292            let owner = <ContractOwner<T>>::get(contract);2293            ensure!(account == owner, Error::<T>::NoPermission);2294        } else {2295            fail!(Error::<T>::NoPermission);2296        }22972298        Ok(())2299    }2300}23012302////////////////////////////////////////////////////////////////////////////////////////////////////2303// Economic models2304// #region23052306/// Fee multiplier.2307pub type Multiplier = FixedU128;23082309type BalanceOf<T> = <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::Balance;23102311/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2312/// in the queue.2313#[derive(Encode, Decode, Clone, Eq, PartialEq)]2314pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);23152316impl<T: Config + Send + Sync> sp_std::fmt::Debug 2317    for ChargeTransactionPayment<T>2318{2319	#[cfg(feature = "std")]2320	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2321		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2322	}2323	#[cfg(not(feature = "std"))]2324	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2325		Ok(())2326	}2327}23282329impl<T: Config> ChargeTransactionPayment<T>2330where2331    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2332    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2333    T::AccountId: AsRef<[u8]>,2334    T::AccountId: UncheckedFrom<T::Hash>,2335{2336    fn traditional_fee(2337        len: usize,2338        info: &DispatchInfoOf<T::Call>,2339        tip: BalanceOf<T>,2340    ) -> BalanceOf<T>2341    where2342        T::Call: Dispatchable<Info = DispatchInfo>,2343    {2344        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2345    }23462347	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2348        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2349        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2350        let len_saturation = max_block_length as u64 / (len as u64).max(1);2351        let coefficient: BalanceOf<T> = weight_saturation2352            .min(len_saturation)2353            .saturated_into::<BalanceOf<T>>();2354        final_fee2355            .saturating_mul(coefficient)2356            .saturated_into::<TransactionPriority>()2357    }23582359    fn withdraw_fee(2360        &self,2361        who: &T::AccountId,2362        call: &T::Call,2363        info: &DispatchInfoOf<T::Call>,2364        len: usize,2365	) -> Result<2366		(2367			BalanceOf<T>,2368			<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2369		),2370		TransactionValidityError,2371	> {2372        let tip = self.0;23732374        // Set fee based on call type. Creating collection costs 1 Unique.2375        // All other transactions have traditional fees so far2376        // let fee = match call.is_sub_type() {2377        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2378        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2379        //                                                 // _ => <BalanceOf<T>>::from(100)2380        // };2381        let fee = Self::traditional_fee(len, info, tip);23822383        // Only mess with balances if fee is not zero.2384        if fee.is_zero() {2385            return <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2386			.map(|i| (fee, i));2387        }23882389        // Determine who is paying transaction fee based on ecnomic model2390        // Parse call to extract collection ID and access collection sponsor2391        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2392            Some(Call::create_item(collection_id, _owner, _properties)) => {23932394                // sponsor timeout2395                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;23962397                let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;2398                let mut sponsored = true;2399                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2400                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2401                    let limit_time = last_tx_block + limit.into();2402                    if block_number <= limit_time {2403                        sponsored = false;2404                    }2405                }2406                if sponsored {2407                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2408                }24092410                // check free create limit2411                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2412                   (<Collection<T>>::get(collection_id).sponsor_confirmed) &&2413                   (sponsored)2414                {2415                    <Collection<T>>::get(collection_id).sponsor2416                } else {2417                    T::AccountId::default()2418                }2419            }2420            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2421                2422                let mut sponsor_transfer = false;2423                if <Collection<T>>::get(collection_id).sponsor_confirmed {24242425                    let collection_limits = <Collection<T>>::get(collection_id).limits;2426                    let collection_mode = <Collection<T>>::get(collection_id).mode;2427    2428                    // sponsor timeout2429                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2430                    sponsor_transfer = match collection_mode {2431                        CollectionMode::NFT => {2432    2433                            // get correct limit2434                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2435                                collection_limits.sponsor_transfer_timeout2436                            } else {2437                                ChainLimit::get().nft_sponsor_transfer_timeout2438                            };2439    2440                            let mut sponsored = true;2441                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2442                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2443                                let limit_time = last_tx_block + limit.into();2444                                if block_number <= limit_time {2445                                    sponsored = false;2446                                }2447                            }2448                            if sponsored {2449                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2450                            }24512452                            sponsored2453                        }2454                        CollectionMode::Fungible(_) => {2455    2456                            // get correct limit2457                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2458                                collection_limits.sponsor_transfer_timeout2459                            } else {2460                                ChainLimit::get().fungible_sponsor_transfer_timeout2461                            };2462    2463                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2464                            let mut sponsored = true;2465                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2466                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2467                                let limit_time = last_tx_block + limit.into();2468                                if block_number <= limit_time {2469                                    sponsored = false;2470                                }2471                            }2472                            if sponsored {2473                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2474                            }24752476                            sponsored2477                        }2478                        CollectionMode::ReFungible => {2479    2480                            // get correct limit2481                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2482                                collection_limits.sponsor_transfer_timeout2483                            } else {2484                                ChainLimit::get().refungible_sponsor_transfer_timeout2485                            };2486    2487                            let mut sponsored = true;2488                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2489                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2490                                let limit_time = last_tx_block + limit.into();2491                                if block_number <= limit_time {2492                                    sponsored = false;2493                                }2494                            }2495                            if sponsored {2496                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2497                            }24982499                            sponsored2500                        }2501                        _ => {2502                            false2503                        },2504                    };2505                }25062507                if !sponsor_transfer {2508                    T::AccountId::default()2509                } else {2510                    <Collection<T>>::get(collection_id).sponsor2511                }2512            }25132514            _ => T::AccountId::default(),2515        };25162517        // Sponsor smart contracts2518        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {25192520            // On instantiation: set the contract owner2521            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {25222523                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2524                    &who,2525                    code_hash,2526                    salt,2527                );2528                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());25292530                T::AccountId::default()2531            },25322533            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2534            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {25352536                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());25372538                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2539                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2540                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2541                  2542                if !owned_contract && white_list_enabled {2543                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2544                        return Err(InvalidTransaction::Call.into());2545                    }2546                }25472548                let mut sponsor_transfer = false;2549                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2550                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2551                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2552                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2553                    let limit_time = last_tx_block + rate_limit;25542555                    if block_number >= limit_time {2556                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2557                        sponsor_transfer = true;2558                    }2559                } else {2560                    sponsor_transfer = false;2561                }2562               2563                2564                let mut sp = T::AccountId::default();2565                if sponsor_transfer {2566                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2567                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2568                            sp = called_contract;2569                        }2570                    }2571                }25722573                sp2574            },25752576            _ => sponsor,2577        };25782579        let mut who_pays_fee: T::AccountId = sponsor.clone();2580        if sponsor == T::AccountId::default() {2581            who_pays_fee = who.clone();2582        }25832584		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2585			.map(|i| (fee, i))2586    }2587}258825892590impl<T: Config + Send + Sync> SignedExtension2591    for ChargeTransactionPayment<T>2592where2593    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2594    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2595    T::AccountId: AsRef<[u8]>,2596    T::AccountId: UncheckedFrom<T::Hash>,2597{2598    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2599    type AccountId = T::AccountId;2600    type Call = T::Call;2601    type AdditionalSigned = ();2602    type Pre = (2603        // tip2604        BalanceOf<T>,2605        // who pays fee2606        Self::AccountId,2607		// imbalance resulting from withdrawing the fee2608		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2609    );2610    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2611        Ok(())2612    }26132614    fn validate(2615        &self,2616        who: &Self::AccountId,2617        call: &Self::Call,2618        info: &DispatchInfoOf<Self::Call>,2619        len: usize,2620    ) -> TransactionValidity {2621		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2622		Ok(ValidTransaction {2623			priority: Self::get_priority(len, info, fee),2624			..Default::default()2625		})2626    }26272628    fn pre_dispatch(2629        self,2630        who: &Self::AccountId,2631        call: &Self::Call,2632        info: &DispatchInfoOf<Self::Call>,2633        len: usize,2634    ) -> Result<Self::Pre, TransactionValidityError> {2635        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2636        Ok((self.0, who.clone(), imbalance))2637    }26382639    fn post_dispatch(2640        pre: Self::Pre,2641        info: &DispatchInfoOf<Self::Call>,2642        post_info: &PostDispatchInfoOf<Self::Call>,2643        len: usize,2644        _result: &DispatchResult,2645    ) -> Result<(), TransactionValidityError> {2646		let (tip, who, imbalance) = pre;2647		let actual_fee = transaction_payment::Module::<T>::compute_actual_fee(2648			len as u32,2649			info,2650			post_info,2651			tip,2652		);2653		<T as transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2654		Ok(())2655    }2656}26572658// #endregion
modifiedtests/src/approve.test.tsdiffbeforeafterboth
--- a/tests/src/approve.test.ts
+++ b/tests/src/approve.test.ts
@@ -4,7 +4,6 @@
 //
 import { IKeyringPair } from '@polkadot/types/types';
 import { ApiPromise } from '@polkadot/api';
-import BN from 'bn.js';
 import chai from 'chai';
 import chaiAsPromised from 'chai-as-promised';
 import privateKey from './substrate/privateKey';
@@ -13,66 +12,61 @@
   approveExpectFail,
   approveExpectSuccess,
   createCollectionExpectSuccess,
-  createFungibleItemExpectSuccess,
   createItemExpectSuccess,
   destroyCollectionExpectSuccess,
   transferExpectSuccess,
-  transferFromExpectSuccess,
-  U128_MAX,
 } from './util/helpers';
 
 chai.use(chaiAsPromised);
-const expect = chai.expect;
 
-describe('Integration Test approve(spender, collection_id, item_id, amount):', () => {
-  it('Execute the extrinsic and check approvedList', async () => {
-    await usingApi(async (api: ApiPromise) => {
-      const Alice = privateKey('//Alice');
-      const Bob = privateKey('//Bob');
-      const nftCollectionId = await createCollectionExpectSuccess();
-      // nft
-      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
-      await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob);
-      // fungible
-      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
-      const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
-      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob);
-      // reFungible
-      const reFungibleCollectionId =
-        await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
-      const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
-      await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob);
+let Alice: IKeyringPair;
+let Bob: IKeyringPair;
+
+describe.only('Integration Test approve(spender, collection_id, item_id, amount):', () => {
+  before(async () => {
+    await usingApi(async () => {
+      Alice = privateKey('//Alice');
+      Bob = privateKey('//Bob');
     });
   });
 
+  it('Execute the extrinsic and check approvedList', async () => {
+    const nftCollectionId = await createCollectionExpectSuccess();
+    // nft
+    const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+    await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob);
+    // fungible
+    const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+    const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+    await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob);
+    // reFungible
+    const reFungibleCollectionId =
+      await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
+    const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+    await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob);
+  });
+
   it('Remove approval by using 0 amount', async () => {
-    await usingApi(async (api: ApiPromise) => {
-      const Alice = privateKey('//Alice');
-      const Bob = privateKey('//Bob');
-      const nftCollectionId = await createCollectionExpectSuccess();
-      // nft
-      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
-      await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob, 1);
-      await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob, 0);
-      // fungible
-      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
-      const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
-      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1);
-      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 0);
-      // reFungible
-      const reFungibleCollectionId =
-        await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
-      const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
-      await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob, 1);
-      await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob, 0);
-    });
+    const nftCollectionId = await createCollectionExpectSuccess();
+    // nft
+    const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+    await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob, 1);
+    await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob, 0);
+    // fungible
+    const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+    const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+    await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1);
+    await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 0);
+    // reFungible
+    const reFungibleCollectionId =
+      await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
+    const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+    await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob, 1);
+    await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob, 0);
   });
 });
 
-describe('Negative Integration Test approve(spender, collection_id, item_id, amount):', () => {
-  let Alice: IKeyringPair;
-  let Bob: IKeyringPair;
-
+describe.only('Negative Integration Test approve(spender, collection_id, item_id, amount):', () => {
   before(async () => {
     await usingApi(async (api) => {
       Alice = privateKey('//Alice');
@@ -82,7 +76,6 @@
 
   it('Approve for a collection that does not exist', async () => {
     await usingApi(async (api: ApiPromise) => {
-
       // nft
       const nftCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
       await approveExpectFail(nftCollectionCount + 1, 1, Alice, Bob);
@@ -96,56 +89,48 @@
   });
 
   it('Approve for a collection that was destroyed', async () => {
-    await usingApi(async (api: ApiPromise) => {
-      // nft
-      const nftCollectionId = await createCollectionExpectSuccess();
-      await destroyCollectionExpectSuccess(nftCollectionId);
-      await approveExpectFail(nftCollectionId, 1, Alice, Bob);
-      // fungible
-      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
-      await destroyCollectionExpectSuccess(fungibleCollectionId);
-      await approveExpectFail(fungibleCollectionId, 1, Alice, Bob);
-      // reFungible
-      const reFungibleCollectionId =
-        await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
-      await destroyCollectionExpectSuccess(reFungibleCollectionId);
-      await approveExpectFail(reFungibleCollectionId, 1, Alice, Bob);
-    });
+    // nft
+    const nftCollectionId = await createCollectionExpectSuccess();
+    await destroyCollectionExpectSuccess(nftCollectionId);
+    await approveExpectFail(nftCollectionId, 1, Alice, Bob);
+    // fungible
+    const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+    await destroyCollectionExpectSuccess(fungibleCollectionId);
+    await approveExpectFail(fungibleCollectionId, 1, Alice, Bob);
+    // reFungible
+    const reFungibleCollectionId =
+      await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
+    await destroyCollectionExpectSuccess(reFungibleCollectionId);
+    await approveExpectFail(reFungibleCollectionId, 1, Alice, Bob);
   });
 
   it('Approve transfer of a token that does not exist', async () => {
-    await usingApi(async (api: ApiPromise) => {
-      // nft
-      const nftCollectionId = await createCollectionExpectSuccess();
-      await approveExpectFail(nftCollectionId, 2, Alice, Bob);
-      // fungible
-      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
-      await approveExpectFail(fungibleCollectionId, 2, Alice, Bob);
-      // reFungible
-      const reFungibleCollectionId =
-        await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
-      await approveExpectFail(reFungibleCollectionId, 2, Alice, Bob);
-    });
+    // nft
+    const nftCollectionId = await createCollectionExpectSuccess();
+    await approveExpectFail(nftCollectionId, 2, Alice, Bob);
+    // fungible
+    const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+    await approveExpectFail(fungibleCollectionId, 2, Alice, Bob);
+    // reFungible
+    const reFungibleCollectionId =
+      await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
+    await approveExpectFail(reFungibleCollectionId, 2, Alice, Bob);
   });
 
   it('Approve using the address that does not own the approved token', async () => {
-    await usingApi(async (api: ApiPromise) => {
-      const Alice = privateKey('//Alice');
-      const Bob = privateKey('//Bob');
-      const nftCollectionId = await createCollectionExpectSuccess();
-      // nft
-      const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
-      await approveExpectFail(nftCollectionId, newNftTokenId, Bob, Alice);
-      // fungible
-      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
-      const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
-      await approveExpectFail(fungibleCollectionId, newFungibleTokenId, Bob, Alice);
-      // reFungible
-      const reFungibleCollectionId =
-        await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
-      const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
-      await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, Bob, Alice);
-    });
+    const nftCollectionId = await createCollectionExpectSuccess();
+    // nft
+    const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
+    await approveExpectFail(nftCollectionId, newNftTokenId, Bob, Alice);
+    // fungible
+    const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+    const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
+    await approveExpectFail(fungibleCollectionId, newFungibleTokenId, Bob, Alice);
+    // reFungible
+    const reFungibleCollectionId =
+      await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
+    const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
+    await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, Bob, Alice);
   });
 
   it('should fail if approved more NFTs than owned', async () => {