git.delta.rocks / unique-network / refs/commits / 8d3b0d18cfc9

difftreelog

source

pallets/nft/src/lib.rs98.2 KiBsourcehistory
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, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub struct CollectionType<T: Config> {129    pub owner: T::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: T::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<T::BlockNumber>, // 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<BlockNumber: Encode + Decode> {182    pub account_token_ownership_limit: u32,183    pub sponsored_data_size: u32,184    /// None - setVariableMetadata is not sponsored185    /// Some(v) - setVariableMetadata is sponsored 186    ///           if there is v block between txs187    pub sponsored_data_rate_limit: Option<BlockNumber>,188    pub token_limit: u32,189190    // Timeouts for item types in passed blocks191    pub sponsor_transfer_timeout: u32,192    pub owner_can_transfer: bool,193    pub owner_can_destroy: bool,194}195196impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {197    fn default() -> Self {198        Self { 199            account_token_ownership_limit: 10_000_000, 200            token_limit: u32::max_value(),201            sponsored_data_size: u32::max_value(), 202            sponsored_data_rate_limit: None,203            sponsor_transfer_timeout: 14400,204            owner_can_transfer: true,205            owner_can_destroy: true206        }207    }208}209210#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]211#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]212pub struct ChainLimits {213    pub collection_numbers_limit: u32,214    pub account_token_ownership_limit: u32,215    pub collections_admins_limit: u64,216    pub custom_data_limit: u32,217218    // Timeouts for item types in passed blocks219    pub nft_sponsor_transfer_timeout: u32,220    pub fungible_sponsor_transfer_timeout: u32,221    pub refungible_sponsor_transfer_timeout: u32,222223    // Schema limits224    pub offchain_schema_limit: u32,225    pub variable_on_chain_schema_limit: u32,226    pub const_on_chain_schema_limit: u32,227}228229pub trait WeightInfo {230	fn create_collection() -> Weight;231	fn destroy_collection() -> Weight;232	fn add_to_white_list() -> Weight;233	fn remove_from_white_list() -> Weight;234    fn set_public_access_mode() -> Weight;235    fn set_mint_permission() -> Weight;236    fn change_collection_owner() -> Weight;237    fn add_collection_admin() -> Weight;238    fn remove_collection_admin() -> Weight;239    fn set_collection_sponsor() -> Weight;240    fn confirm_sponsorship() -> Weight;241    fn remove_collection_sponsor() -> Weight;242    fn create_item(s: usize) -> Weight;243    fn burn_item() -> Weight;244    fn transfer() -> Weight;245    fn approve() -> Weight;246    fn transfer_from() -> Weight;247    fn set_offchain_schema() -> Weight;248    fn set_const_on_chain_schema() -> Weight;249    fn set_variable_on_chain_schema() -> Weight;250    fn set_variable_meta_data() -> Weight;251    fn enable_contract_sponsoring() -> Weight;252    fn set_schema_version() -> Weight;253    fn set_chain_limits() -> Weight;254    fn set_contract_sponsoring_rate_limit() -> Weight;255    fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;256    fn toggle_contract_white_list() -> Weight;257    fn add_to_contract_white_list() -> Weight;258    fn remove_from_contract_white_list() -> Weight;259    fn set_collection_limits() -> Weight;260}261262#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]263#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]264pub struct CreateNftData {265    pub const_data: Vec<u8>,266    pub variable_data: Vec<u8>,267}268269#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]270#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]271pub struct CreateFungibleData {272    pub value: u128,273}274275#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]276#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]277pub struct CreateReFungibleData {278    pub const_data: Vec<u8>,279    pub variable_data: Vec<u8>,280    pub pieces: u128,281}282283#[derive(Encode, Decode, Debug, Clone, PartialEq)]284#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]285pub enum CreateItemData {286    NFT(CreateNftData),287    Fungible(CreateFungibleData),288    ReFungible(CreateReFungibleData),289}290291impl CreateItemData {292    pub fn len(&self) -> usize {293        let len = match self {294            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),295            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),296            _ => 0297        };298        299        return len;300    }301}302303impl From<CreateNftData> for CreateItemData {304    fn from(item: CreateNftData) -> Self {305        CreateItemData::NFT(item)306    }307}308309impl From<CreateReFungibleData> for CreateItemData {310    fn from(item: CreateReFungibleData) -> Self {311        CreateItemData::ReFungible(item)312    }313}314315impl From<CreateFungibleData> for CreateItemData {316    fn from(item: CreateFungibleData) -> Self {317        CreateItemData::Fungible(item)318    }319}320321322decl_error! {323	/// Error for non-fungible-token module.324	pub enum Error for Module<T: Config> {325        /// Total collections bound exceeded.326        TotalCollectionsLimitExceeded,327		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.328        CollectionDecimalPointLimitExceeded, 329        /// Collection name can not be longer than 63 char.330        CollectionNameLimitExceeded, 331        /// Collection description can not be longer than 255 char.332        CollectionDescriptionLimitExceeded, 333        /// Token prefix can not be longer than 15 char.334        CollectionTokenPrefixLimitExceeded,335        /// This collection does not exist.336        CollectionNotFound,337        /// Item not exists.338        TokenNotFound,339        /// Admin not found340        AdminNotFound,341        /// Arithmetic calculation overflow.342        NumOverflow,       343        /// Account already has admin role.344        AlreadyAdmin,  345        /// You do not own this collection.346        NoPermission,347        /// This address is not set as sponsor, use setCollectionSponsor first.348        ConfirmUnsetSponsorFail,349        /// Collection is not in mint mode.350        PublicMintingNotAllowed,351        /// Sender parameter and item owner must be equal.352        MustBeTokenOwner,353        /// Item balance not enough.354        TokenValueTooLow,355        /// Size of item is too large.356        NftSizeLimitExceeded,357        /// No approve found358        ApproveNotFound,359        /// Requested value more than approved.360        TokenValueNotEnough,361        /// Only approved addresses can call this method.362        ApproveRequired,363        /// Address is not in white list.364        AddresNotInWhiteList,365        /// Number of collection admins bound exceeded.366        CollectionAdminsLimitExceeded,367        /// Owned tokens by a single address bound exceeded.368        AddressOwnershipLimitExceeded,369        /// Length of items properties must be greater than 0.370        EmptyArgument,371        /// const_data exceeded data limit.372        TokenConstDataLimitExceeded,373        /// variable_data exceeded data limit.374        TokenVariableDataLimitExceeded,375        /// Not NFT item data used to mint in NFT collection.376        NotNftDataUsedToMintNftCollectionToken,377        /// Not Fungible item data used to mint in Fungible collection.378        NotFungibleDataUsedToMintFungibleCollectionToken,379        /// Not Re Fungible item data used to mint in Re Fungible collection.380        NotReFungibleDataUsedToMintReFungibleCollectionToken,381        /// Unexpected collection type.382        UnexpectedCollectionType,383        /// Can't store metadata in fungible tokens.384        CantStoreMetadataInFungibleTokens,385        /// Collection token limit exceeded386        CollectionTokenLimitExceeded,387        /// Account token limit exceeded per collection388        AccountTokenLimitExceeded,389        /// Collection limit bounds per collection exceeded390        CollectionLimitBoundsExceeded,391        /// Schema data size limit bound exceeded392        SchemaDataLimitExceeded,393        /// Maximum refungibility exceeded394        WrongRefungiblePieces395	}396}397398pub trait Config: system::Config + Sized + transaction_payment::Config + pallet_contracts::Config {399    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;400401    /// Weight information for extrinsics in this pallet.402	type WeightInfo: WeightInfo;403}404405#[cfg(feature = "runtime-benchmarks")]406mod benchmarking;407408// #endregion409410decl_storage! {411    trait Store for Module<T: Config> as Nft {412413        // Private members414        NextCollectionID: CollectionId;415        CreatedCollectionCount: u32;416        ChainVersion: u64;417        ItemListIndex: map hasher(identity) CollectionId => TokenId;418419        // Chain limits struct420        pub ChainLimit get(fn chain_limit) config(): ChainLimits;421422        // Bound counters423        CollectionCount: u32;424        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;425426        // Basic collections427        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => Option<CollectionType<T>> = None;428        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;429        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;430431        /// Balance owner per collection map432        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;433434        /// second parameter: item id + owner account id + spender account id435        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;436437        /// Item collections438        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;439        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;440        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;441442        /// Index list443        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;444445        /// Tokens transfer baskets446        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;447        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;448        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;449        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;450451        /// Variable metadata sponsoring452        pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Option<T::BlockNumber> = None;453454        // Contract Sponsorship and Ownership455        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;456        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;457        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;458        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;459        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 460        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 461    }462    add_extra_genesis {463        build(|config: &GenesisConfig<T>| {464            // Modification of storage465            for (_num, _c) in &config.collection {466                <Module<T>>::init_collection(_c);467            }468469            for (_num, _c, _i) in &config.nft_item_id {470                <Module<T>>::init_nft_token(*_c, _i);471            }472473            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {474                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);475            }476477            for (_num, _c, _i) in &config.refungible_item_id {478                <Module<T>>::init_refungible_token(*_c, _i);479            }480        })481    }482}483484decl_event!(485    pub enum Event<T>486    where487        AccountId = <T as system::Config>::AccountId,488    {489        /// New collection was created490        /// 491        /// # Arguments492        /// 493        /// * collection_id: Globally unique identifier of newly created collection.494        /// 495        /// * mode: [CollectionMode] converted into u8.496        /// 497        /// * account_id: Collection owner.498        Created(CollectionId, u8, AccountId),499500        /// New item was created.501        /// 502        /// # Arguments503        /// 504        /// * collection_id: Id of the collection where item was created.505        /// 506        /// * item_id: Id of an item. Unique within the collection.507        ItemCreated(CollectionId, TokenId),508509        /// Collection item was burned.510        /// 511        /// # Arguments512        /// 513        /// collection_id.514        /// 515        /// item_id: Identifier of burned NFT.516        ItemDestroyed(CollectionId, TokenId),517    }518);519520decl_module! {521    pub struct Module<T: Config> for enum Call 522    where 523        origin: T::Origin524    {525        fn deposit_event() = default;526        type Error = Error<T>;527528        fn on_initialize(now: T::BlockNumber) -> Weight {529530            if ChainVersion::get() < 2531            {532                let value = NextCollectionID::get();533                CreatedCollectionCount::put(value);534                ChainVersion::put(2);535            }536537            0538        }539540        /// 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.541        /// 542        /// # Permissions543        /// 544        /// * Anyone.545        /// 546        /// # Arguments547        /// 548        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.549        /// 550        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.551        /// 552        /// * token_prefix: UTF-8 string with token prefix.553        /// 554        /// * mode: [CollectionMode] collection type and type dependent data.555        // returns collection ID556        #[weight = <T as Config>::WeightInfo::create_collection()]557        pub fn create_collection(origin,558                                 collection_name: Vec<u16>,559                                 collection_description: Vec<u16>,560                                 token_prefix: Vec<u8>,561                                 mode: CollectionMode) -> DispatchResult {562563            // Anyone can create a collection564            let who = ensure_signed(origin)?;565566            let decimal_points = match mode {567                CollectionMode::Fungible(points) => points,568                _ => 0569            };570571            // bound Total number of collections572            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);573574            // check params575            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);576            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);577            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);578            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);579580            // Generate next collection ID581            let next_id = CreatedCollectionCount::get()582                .checked_add(1)583                .ok_or(Error::<T>::NumOverflow)?;584585            // bound counter586            let total = CollectionCount::get()587                .checked_add(1)588                .ok_or(Error::<T>::NumOverflow)?;589590            CreatedCollectionCount::put(next_id);591            CollectionCount::put(total);592593            // Create new collection594            let new_collection = CollectionType {595                owner: who.clone(),596                name: collection_name,597                mode: mode.clone(),598                mint_mode: false,599                access: AccessMode::Normal,600                description: collection_description,601                decimal_points: decimal_points,602                token_prefix: token_prefix,603                offchain_schema: Vec::new(),604                schema_version: SchemaVersion::ImageURL,605                sponsor: T::AccountId::default(),606                sponsor_confirmed: false,607                variable_on_chain_schema: Vec::new(),608                const_on_chain_schema: Vec::new(),609                limits: CollectionLimits::default()610            };611612            // Add new collection to map613            <Collection<T>>::insert(next_id, new_collection);614615            // call event616            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));617618            Ok(())619        }620621        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.622        /// 623        /// # Permissions624        /// 625        /// * Collection Owner.626        /// 627        /// # Arguments628        /// 629        /// * collection_id: collection to destroy.630        #[weight = <T as Config>::WeightInfo::destroy_collection()]631        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {632633            let sender = ensure_signed(origin)?;634            Self::check_owner_permissions(collection_id, sender)?;635636            <AddressTokens<T>>::remove_prefix(collection_id);637            <Allowances<T>>::remove_prefix(collection_id);638            <Balance<T>>::remove_prefix(collection_id);639            <ItemListIndex>::remove(collection_id);640            <AdminList<T>>::remove(collection_id);641            <Collection<T>>::remove(collection_id);642            <WhiteList<T>>::remove_prefix(collection_id);643644            <NftItemList<T>>::remove_prefix(collection_id);645            <FungibleItemList<T>>::remove_prefix(collection_id);646            <ReFungibleItemList<T>>::remove_prefix(collection_id);647648            <NftTransferBasket<T>>::remove_prefix(collection_id);649            <FungibleTransferBasket<T>>::remove_prefix(collection_id);650            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);651652            <VariableMetaDataBasket<T>>::remove_prefix(collection_id);653654            if CollectionCount::get() > 0655            {656                // bound couter657                let total = CollectionCount::get()658                    .checked_sub(1)659                    .ok_or(Error::<T>::NumOverflow)?;660661                CollectionCount::put(total);662            }663664            Ok(())665        }666667        /// Add an address to white list.668        /// 669        /// # Permissions670        /// 671        /// * Collection Owner672        /// * Collection Admin673        /// 674        /// # Arguments675        /// 676        /// * collection_id.677        /// 678        /// * address.679        #[weight = <T as Config>::WeightInfo::add_to_white_list()]680        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{681682            let sender = ensure_signed(origin)?;683            Self::check_owner_or_admin_permissions(collection_id, sender)?;684685            <WhiteList<T>>::insert(collection_id, address, true);686            687            Ok(())688        }689690        /// Remove an address from white list.691        /// 692        /// # Permissions693        /// 694        /// * Collection Owner695        /// * Collection Admin696        /// 697        /// # Arguments698        /// 699        /// * collection_id.700        /// 701        /// * address.702        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]703        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{704705            let sender = ensure_signed(origin)?;706            Self::check_owner_or_admin_permissions(collection_id, sender)?;707708            <WhiteList<T>>::remove(collection_id, address);709710            Ok(())711        }712713        /// Toggle between normal and white list access for the methods with access for `Anyone`.714        /// 715        /// # Permissions716        /// 717        /// * Collection Owner.718        /// 719        /// # Arguments720        /// 721        /// * collection_id.722        /// 723        /// * mode: [AccessMode]724        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]725        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult726        {727            let sender = ensure_signed(origin)?;728729            Self::check_owner_permissions(collection_id, sender)?;730            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();731            target_collection.access = mode;732            <Collection<T>>::insert(collection_id, target_collection);733734            Ok(())735        }736737        /// Allows Anyone to create tokens if:738        /// * White List is enabled, and739        /// * Address is added to white list, and740        /// * This method was called with True parameter741        /// 742        /// # Permissions743        /// * Collection Owner744        ///745        /// # Arguments746        /// 747        /// * collection_id.748        /// 749        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.750        #[weight = <T as Config>::WeightInfo::set_mint_permission()]751        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult752        {753            let sender = ensure_signed(origin)?;754755            Self::check_owner_permissions(collection_id, sender)?;756            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();757            target_collection.mint_mode = mint_permission;758            <Collection<T>>::insert(collection_id, target_collection);759760            Ok(())761        }762763        /// Change the owner of the collection.764        /// 765        /// # Permissions766        /// 767        /// * Collection Owner.768        /// 769        /// # Arguments770        /// 771        /// * collection_id.772        /// 773        /// * new_owner.774        #[weight = <T as Config>::WeightInfo::change_collection_owner()]775        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {776777            let sender = ensure_signed(origin)?;778            Self::check_owner_permissions(collection_id, sender)?;779            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();780            target_collection.owner = new_owner;781            <Collection<T>>::insert(collection_id, target_collection);782783            Ok(())784        }785786        /// Adds an admin of the Collection.787        /// 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. 788        /// 789        /// # Permissions790        /// 791        /// * Collection Owner.792        /// * Collection Admin.793        /// 794        /// # Arguments795        /// 796        /// * collection_id: ID of the Collection to add admin for.797        /// 798        /// * new_admin_id: Address of new admin to add.799        #[weight = <T as Config>::WeightInfo::add_collection_admin()]800        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {801802            let sender = ensure_signed(origin)?;803            Self::check_owner_or_admin_permissions(collection_id, sender)?;804            let mut admin_arr: Vec<T::AccountId> = Vec::new();805806            if <AdminList<T>>::contains_key(collection_id)807            {808                admin_arr = <AdminList<T>>::get(collection_id);809                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);810            }811812            // Number of collection admins813            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);814815            admin_arr.push(new_admin_id);816            <AdminList<T>>::insert(collection_id, admin_arr);817818            Ok(())819        }820821        /// 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.822        ///823        /// # Permissions824        /// 825        /// * Collection Owner.826        /// * Collection Admin.827        /// 828        /// # Arguments829        /// 830        /// * collection_id: ID of the Collection to remove admin for.831        /// 832        /// * account_id: Address of admin to remove.833        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]834        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {835836            let sender = ensure_signed(origin)?;837            Self::check_owner_or_admin_permissions(collection_id, sender)?;838            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);839840            let mut admin_arr = <AdminList<T>>::get(collection_id);841            admin_arr.retain(|i| *i != account_id);842            <AdminList<T>>::insert(collection_id, admin_arr);843844            Ok(())845        }846847        /// # Permissions848        /// 849        /// * Collection Owner850        /// 851        /// # Arguments852        /// 853        /// * collection_id.854        /// 855        /// * new_sponsor.856        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]857        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {858859            let sender = ensure_signed(origin)?;860861            let mut target_collection = <Collection<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?;862            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);863864            target_collection.sponsor = new_sponsor;865            target_collection.sponsor_confirmed = false;866            <Collection<T>>::insert(collection_id, target_collection);867868            Ok(())869        }870871        /// # Permissions872        /// 873        /// * Sponsor.874        /// 875        /// # Arguments876        /// 877        /// * collection_id.878        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]879        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {880881            let sender = ensure_signed(origin)?;882883            let mut target_collection = <Collection<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?;884            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);885886            target_collection.sponsor_confirmed = true;887            <Collection<T>>::insert(collection_id, target_collection);888889            Ok(())890        }891892        /// Switch back to pay-per-own-transaction model.893        ///894        /// # Permissions895        ///896        /// * Collection owner.897        /// 898        /// # Arguments899        /// 900        /// * collection_id.901        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]902        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {903904            let sender = ensure_signed(origin)?;905906            let mut target_collection = <Collection<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?;907            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);908909            target_collection.sponsor = T::AccountId::default();910            target_collection.sponsor_confirmed = false;911            <Collection<T>>::insert(collection_id, target_collection);912913            Ok(())914        }915916        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.917        /// 918        /// # Permissions919        /// 920        /// * Collection Owner.921        /// * Collection Admin.922        /// * Anyone if923        ///     * White List is enabled, and924        ///     * Address is added to white list, and925        ///     * MintPermission is enabled (see SetMintPermission method)926        /// 927        /// # Arguments928        /// 929        /// * collection_id: ID of the collection.930        /// 931        /// * owner: Address, initial owner of the NFT.932        ///933        /// * data: Token data to store on chain.934        // #[weight =935        // (130_000_000 as Weight)936        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))937        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))938        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]939940        #[weight = <T as Config>::WeightInfo::create_item(data.len())]941        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {942943            let sender = ensure_signed(origin)?;944945            Self::collection_exists(collection_id)?;946947            let target_collection = <Collection<T>>::get(collection_id).unwrap();948949            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;950            Self::validate_create_item_args(&target_collection, &data)?;951            Self::create_item_no_validation(collection_id, owner, data)?;952953            Ok(())954        }955956        /// This method creates multiple instances of NFT Collection created with CreateCollection method.957        /// 958        /// # Permissions959        /// 960        /// * Collection Owner.961        /// * Collection Admin.962        /// * Anyone if963        ///     * White List is enabled, and964        ///     * Address is added to white list, and965        ///     * MintPermission is enabled (see SetMintPermission method)966        /// 967        /// # Arguments968        /// 969        /// * collection_id: ID of the collection.970        /// 971        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].972        /// 973        /// * owner: Address, initial owner of the NFT.974        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()975                               .map(|data| { data.len() })976                               .sum())]977        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {978979            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);980            let sender = ensure_signed(origin)?;981982            Self::collection_exists(collection_id)?;983            let target_collection = <Collection<T>>::get(collection_id).unwrap();984985            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;986987            for data in &items_data {988                Self::validate_create_item_args(&target_collection, data)?;989            }990            for data in &items_data {991                Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?;992            }993994            Ok(())995        }996997        /// Destroys a concrete instance of NFT.998        /// 999        /// # Permissions1000        /// 1001        /// * Collection Owner.1002        /// * Collection Admin.1003        /// * Current NFT Owner.1004        /// 1005        /// # Arguments1006        /// 1007        /// * collection_id: ID of the collection.1008        /// 1009        /// * item_id: ID of NFT to burn.1010        #[weight = <T as Config>::WeightInfo::burn_item()]1011        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10121013            let sender = ensure_signed(origin)?;1014            Self::collection_exists(collection_id)?;10151016            // Transfer permissions check1017            let target_collection = <Collection<T>>::get(collection_id).unwrap();1018            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1019                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1020                Error::<T>::NoPermission);10211022            if target_collection.access == AccessMode::WhiteList {1023                Self::check_white_list(collection_id, &sender)?;1024            }10251026            match target_collection.mode1027            {1028                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1029                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1030                CollectionMode::ReFungible  => Self::burn_refungible_item(collection_id, item_id, &sender)?,1031                _ => ()1032            };10331034            // call event1035            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10361037            Ok(())1038        }10391040        /// Change ownership of the token.1041        /// 1042        /// # Permissions1043        /// 1044        /// * Collection Owner1045        /// * Collection Admin1046        /// * Current NFT owner1047        ///1048        /// # Arguments1049        /// 1050        /// * recipient: Address of token recipient.1051        /// 1052        /// * collection_id.1053        /// 1054        /// * item_id: ID of the item1055        ///     * Non-Fungible Mode: Required.1056        ///     * Fungible Mode: Ignored.1057        ///     * Re-Fungible Mode: Required.1058        /// 1059        /// * value: Amount to transfer.1060        ///     * Non-Fungible Mode: Ignored1061        ///     * Fungible Mode: Must specify transferred amount1062        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1063        #[weight = <T as Config>::WeightInfo::transfer()]1064        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1065            let sender = ensure_signed(origin)?;1066            Self::transfer_internal(sender, recipient, collection_id, item_id, value)1067        }10681069        /// Set, change, or remove approved address to transfer the ownership of the NFT.1070        /// 1071        /// # Permissions1072        /// 1073        /// * Collection Owner1074        /// * Collection Admin1075        /// * Current NFT owner1076        /// 1077        /// # Arguments1078        /// 1079        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1080        /// 1081        /// * collection_id.1082        /// 1083        /// * item_id: ID of the item.1084        #[weight = <T as Config>::WeightInfo::approve()]1085        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {10861087            let sender = ensure_signed(origin)?;10881089            Self::collection_exists(collection_id)?;1090            Self::token_exists(collection_id, item_id, &sender)?;10911092            // Transfer permissions check1093            let target_collection = <Collection<T>>::get(collection_id).unwrap();1094            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1095                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1096                Error::<T>::NoPermission);10971098            if target_collection.access == AccessMode::WhiteList {1099                Self::check_white_list(collection_id, &sender)?;1100                Self::check_white_list(collection_id, &spender)?;1101            }11021103            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1104            let mut allowance: u128 = amount;1105            if allowance_exists {1106                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1107            }1108            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11091110            Ok(())1111        }1112        1113        /// 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.1114        /// 1115        /// # Permissions1116        /// * Collection Owner1117        /// * Collection Admin1118        /// * Current NFT owner1119        /// * Address approved by current NFT owner1120        /// 1121        /// # Arguments1122        /// 1123        /// * from: Address that owns token.1124        /// 1125        /// * recipient: Address of token recipient.1126        /// 1127        /// * collection_id.1128        /// 1129        /// * item_id: ID of the item.1130        /// 1131        /// * value: Amount to transfer.1132        #[weight = <T as Config>::WeightInfo::transfer_from()]1133        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11341135            let sender = ensure_signed(origin)?;1136            let mut appoved_transfer = false;11371138            // Check approval1139            let mut approval: u128 = 0;1140            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1141                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1142                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1143                appoved_transfer = true;1144            }11451146            let target_collection = <Collection<T>>::get(collection_id).unwrap();11471148            // Limits check1149            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11501151            // Transfer permissions check         1152            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1153                Error::<T>::NoPermission);11541155            if target_collection.access == AccessMode::WhiteList {1156                Self::check_white_list(collection_id, &sender)?;1157                Self::check_white_list(collection_id, &recipient)?;1158            }11591160            // Reduce approval by transferred amount or remove if remaining approval drops to 01161            if approval.checked_sub(value).unwrap_or(0) > 0 {1162                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1163            }1164            else {1165                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1166            }11671168            match target_collection.mode1169            {1170                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1171                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1172                CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1173                _ => ()1174            };11751176            Ok(())1177        }11781179        // #[weight = 0]1180        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11811182        //     // let no_perm_mes = "You do not have permissions to modify this collection";1183        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1184        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1185        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11861187        //     // // on_nft_received  call11881189        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;11901191        //     Ok(())1192        // }11931194        /// Set off-chain data schema.1195        /// 1196        /// # Permissions1197        /// 1198        /// * Collection Owner1199        /// * Collection Admin1200        /// 1201        /// # Arguments1202        /// 1203        /// * collection_id.1204        /// 1205        /// * schema: String representing the offchain data schema.1206        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1207        pub fn set_variable_meta_data (1208            origin,1209            collection_id: CollectionId,1210            item_id: TokenId,1211            data: Vec<u8>1212        ) -> DispatchResult {1213            let sender = ensure_signed(origin)?;1214            1215            Self::collection_exists(collection_id)?;1216            Self::token_exists(collection_id, item_id, &sender)?;12171218            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12191220            // Modify permissions check1221            let target_collection = <Collection<T>>::get(collection_id).unwrap();1222            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1223                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1224                Error::<T>::NoPermission);12251226            match target_collection.mode1227            {1228                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1229                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1230                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1231                _ => fail!(Error::<T>::UnexpectedCollectionType)1232            };12331234            Ok(())1235        }1236 1237        /// Set schema standard1238        /// ImageURL1239        /// Unique1240        /// 1241        /// # Permissions1242        /// 1243        /// * Collection Owner1244        /// * Collection Admin1245        /// 1246        /// # Arguments1247        /// 1248        /// * collection_id.1249        /// 1250        /// * schema: SchemaVersion: enum1251        #[weight = <T as Config>::WeightInfo::set_schema_version()]1252        pub fn set_schema_version(1253            origin,1254            collection_id: CollectionId,1255            version: SchemaVersion1256        ) -> DispatchResult {1257            let sender = ensure_signed(origin)?;1258            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1259            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();1260            target_collection.schema_version = version;1261            <Collection<T>>::insert(collection_id, target_collection);12621263            Ok(())1264        }12651266        /// Set off-chain data schema.1267        /// 1268        /// # Permissions1269        /// 1270        /// * Collection Owner1271        /// * Collection Admin1272        /// 1273        /// # Arguments1274        /// 1275        /// * collection_id.1276        /// 1277        /// * schema: String representing the offchain data schema.1278        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1279        pub fn set_offchain_schema(1280            origin,1281            collection_id: CollectionId,1282            schema: Vec<u8>1283        ) -> DispatchResult {1284            let sender = ensure_signed(origin)?;1285            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12861287            // check schema limit1288            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");12891290            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();1291            target_collection.offchain_schema = schema;1292            <Collection<T>>::insert(collection_id, target_collection);12931294            Ok(())1295        }12961297        /// Set const on-chain data schema.1298        /// 1299        /// # Permissions1300        /// 1301        /// * Collection Owner1302        /// * Collection Admin1303        /// 1304        /// # Arguments1305        /// 1306        /// * collection_id.1307        /// 1308        /// * schema: String representing the const on-chain data schema.1309        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1310        pub fn set_const_on_chain_schema (1311            origin,1312            collection_id: CollectionId,1313            schema: Vec<u8>1314        ) -> DispatchResult {1315            let sender = ensure_signed(origin)?;1316            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13171318            // check schema limit1319            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");13201321            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();1322            target_collection.const_on_chain_schema = schema;1323            <Collection<T>>::insert(collection_id, target_collection);13241325            Ok(())1326        }13271328        /// Set variable on-chain data schema.1329        /// 1330        /// # Permissions1331        /// 1332        /// * Collection Owner1333        /// * Collection Admin1334        /// 1335        /// # Arguments1336        /// 1337        /// * collection_id.1338        /// 1339        /// * schema: String representing the variable on-chain data schema.1340        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1341        pub fn set_variable_on_chain_schema (1342            origin,1343            collection_id: CollectionId,1344            schema: Vec<u8>1345        ) -> DispatchResult {1346            let sender = ensure_signed(origin)?;1347            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13481349            // check schema limit1350            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");13511352            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();1353            target_collection.variable_on_chain_schema = schema;1354            <Collection<T>>::insert(collection_id, target_collection);13551356            Ok(())1357        }13581359        // Sudo permissions function1360        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1361        pub fn set_chain_limits(1362            origin,1363            limits: ChainLimits1364        ) -> DispatchResult {13651366            #[cfg(not(feature = "runtime-benchmarks"))]1367            ensure_root(origin)?;13681369            <ChainLimit>::put(limits);1370            Ok(())1371        }13721373        /// Enable smart contract self-sponsoring.1374        /// 1375        /// # Permissions1376        /// 1377        /// * Contract Owner1378        /// 1379        /// # Arguments1380        /// 1381        /// * contract address1382        /// * enable flag1383        /// 1384        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1385        pub fn enable_contract_sponsoring(1386            origin,1387            contract_address: T::AccountId,1388            enable: bool1389        ) -> DispatchResult {13901391            let sender = ensure_signed(origin)?;13921393            #[cfg(feature = "runtime-benchmarks")]1394            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13951396            Self::ensure_contract_owned(sender, &contract_address)?;13971398            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1399            Ok(())1400        }14011402        /// Set the rate limit for contract sponsoring to specified number of blocks.1403        /// 1404        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1405        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1406        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1407        /// from contract endowment if there are at least B blocks between such transactions. 1408        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1409        /// 1410        /// # Permissions1411        /// 1412        /// * Contract Owner1413        /// 1414        /// # Arguments1415        /// 1416        /// -`contract_address`: Address of the contract to sponsor1417        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1418        /// 1419        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1420        pub fn set_contract_sponsoring_rate_limit(1421            origin,1422            contract_address: T::AccountId,1423            rate_limit: T::BlockNumber1424        ) -> DispatchResult {1425            let sender = ensure_signed(origin)?;14261427            #[cfg(feature = "runtime-benchmarks")]1428            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14291430            Self::ensure_contract_owned(sender, &contract_address)?;1431            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1432            Ok(())1433        }14341435        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1436        /// 1437        /// # Permissions1438        /// 1439        /// * Address that deployed smart contract.1440        /// 1441        /// # Arguments1442        /// 1443        /// -`contract_address`: Address of the contract.1444        /// 1445        /// - `enable`: .  1446        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1447        pub fn toggle_contract_white_list(1448            origin,1449            contract_address: T::AccountId,1450            enable: bool1451        ) -> DispatchResult {1452            let sender = ensure_signed(origin)?;14531454            #[cfg(feature = "runtime-benchmarks")]1455            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14561457            Self::ensure_contract_owned(sender, &contract_address)?;1458            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1459            Ok(())1460        }1461        1462        /// Add an address to smart contract white list.1463        /// 1464        /// # Permissions1465        /// 1466        /// * Address that deployed smart contract.1467        /// 1468        /// # Arguments1469        /// 1470        /// -`contract_address`: Address of the contract.1471        ///1472        /// -`account_address`: Address to add.1473        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1474        pub fn add_to_contract_white_list(1475            origin,1476            contract_address: T::AccountId,1477            account_address: T::AccountId1478        ) -> DispatchResult {1479            let sender = ensure_signed(origin)?;14801481            #[cfg(feature = "runtime-benchmarks")]1482            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1483            1484            Self::ensure_contract_owned(sender, &contract_address)?;      1485            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1486            Ok(())1487        }14881489        /// Remove an address from smart contract white list.1490        /// 1491        /// # Permissions1492        /// 1493        /// * Address that deployed smart contract.1494        /// 1495        /// # Arguments1496        /// 1497        /// -`contract_address`: Address of the contract.1498        ///1499        /// -`account_address`: Address to remove.1500        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1501        pub fn remove_from_contract_white_list(1502            origin,1503            contract_address: T::AccountId,1504            account_address: T::AccountId1505        ) -> DispatchResult {1506            let sender = ensure_signed(origin)?;15071508            #[cfg(feature = "runtime-benchmarks")]1509            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15101511            Self::ensure_contract_owned(sender, &contract_address)?;1512            <ContractWhiteList<T>>::remove(contract_address, account_address);1513            Ok(())1514        }15151516        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1517        pub fn set_collection_limits(1518            origin,1519            collection_id: u32,1520            limits: CollectionLimits<T::BlockNumber>,1521        ) -> DispatchResult {1522            let sender = ensure_signed(origin)?;1523            Self::check_owner_permissions(collection_id, sender.clone())?;1524            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();1525            let chain_limits = ChainLimit::get();1526            let climits = target_collection.limits;15271528            // collection bounds1529            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1530                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1531                Error::<T>::CollectionLimitBoundsExceeded);15321533            // token_limit   check  prev1534            ensure!(climits.token_limit > limits.token_limit && 1535                limits.token_limit <= chain_limits.account_token_ownership_limit, 1536                Error::<T>::AccountTokenLimitExceeded);15371538            target_collection.limits = limits;1539            <Collection<T>>::insert(collection_id, target_collection);15401541            Ok(())1542        } 1543    }1544}15451546impl<T: Config> Module<T> {15471548    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {15491550        let target_collection = <Collection<T>>::get(collection_id).unwrap();15511552        // Limits check1553        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;15541555        // Transfer permissions check1556        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1557            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1558            Error::<T>::NoPermission);15591560        if target_collection.access == AccessMode::WhiteList {1561            Self::check_white_list(collection_id, &sender)?;1562            Self::check_white_list(collection_id, &recipient)?;1563        }15641565        match target_collection.mode1566        {1567            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1568            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1569            CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1570            _ => ()1571        };15721573        Ok(())1574    }157515761577    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T>, recipient: &T::AccountId) -> DispatchResult {15781579        // check token limit and account token limit1580        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1581        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1582        1583        Ok(())1584    }15851586    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15871588        // check token limit and account token limit1589        let total_items: u32 = ItemListIndex::get(collection_id);1590        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1591        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1592        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15931594        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1595            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1596            Self::check_white_list(collection_id, owner)?;1597            Self::check_white_list(collection_id, sender)?;1598        }15991600        Ok(())1601    }16021603    fn validate_create_item_args(target_collection: &CollectionType<T>, data: &CreateItemData) -> DispatchResult {1604        match target_collection.mode1605        {1606            CollectionMode::NFT => {1607                if let CreateItemData::NFT(data) = data {1608                    // check sizes1609                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1610                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1611                } else {1612                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1613                }1614            },1615            CollectionMode::Fungible(_) => {1616                if let CreateItemData::Fungible(_) = data {1617                } else {1618                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1619                }1620            },1621            CollectionMode::ReFungible => {1622                if let CreateItemData::ReFungible(data) = data {16231624                    // check sizes1625                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1626                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16271628                    // Check refungibility limits1629                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1630                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1631                } else {1632                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1633                }1634            },1635            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1636        };16371638        Ok(())1639    }16401641    fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1642        match data1643        {1644            CreateItemData::NFT(data) => {1645                let item = NftItemType {1646                    owner,1647                    const_data: data.const_data,1648                    variable_data: data.variable_data1649                };16501651                Self::add_nft_item(collection_id, item)?;1652            },1653            CreateItemData::Fungible(data) => {1654                Self::add_fungible_item(collection_id, &owner, data.value)?;1655            },1656            CreateItemData::ReFungible(data) => {1657                let mut owner_list = Vec::new();1658                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});16591660                let item = ReFungibleItemType {1661                    owner: owner_list,1662                    const_data: data.const_data,1663                    variable_data: data.variable_data1664                };16651666                Self::add_refungible_item(collection_id, item)?;1667            }1668        };16691670        // call event1671        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16721673        Ok(())1674    }16751676    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {16771678        // Does new owner already have an account?1679        let mut balance: u128 = 0;1680        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1681            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1682        } 16831684        // Mint 1685        let item = FungibleItemType {1686            value: balance + value1687        };1688        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16891690        // Update balance1691        let new_balance = <Balance<T>>::get(collection_id, owner)1692            .checked_add(value)1693            .ok_or(Error::<T>::NumOverflow)?;1694        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);16951696        Ok(())1697    }16981699    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1700        let current_index = <ItemListIndex>::get(collection_id)1701            .checked_add(1)1702            .ok_or(Error::<T>::NumOverflow)?;1703        let itemcopy = item.clone();17041705        let value = item.owner.first().unwrap().fraction;1706        let owner = item.owner.first().unwrap().owner.clone();17071708        Self::add_token_index(collection_id, current_index, &owner)?;17091710        <ItemListIndex>::insert(collection_id, current_index);1711        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17121713        // Update balance1714        let new_balance = <Balance<T>>::get(collection_id, &owner)1715            .checked_add(value)1716            .ok_or(Error::<T>::NumOverflow)?;1717        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17181719        Ok(())1720    }17211722    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1723        let current_index = <ItemListIndex>::get(collection_id)1724            .checked_add(1)1725            .ok_or(Error::<T>::NumOverflow)?;17261727        let item_owner = item.owner.clone();1728        Self::add_token_index(collection_id, current_index, &item.owner)?;17291730        <ItemListIndex>::insert(collection_id, current_index);1731        <NftItemList<T>>::insert(collection_id, current_index, item);17321733        // Update balance1734        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1735            .checked_add(1)1736            .ok_or(Error::<T>::NumOverflow)?;1737        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17381739        Ok(())1740    }17411742    fn burn_refungible_item(1743        collection_id: CollectionId,1744        item_id: TokenId,1745        owner: &T::AccountId,1746    ) -> DispatchResult {1747        ensure!(1748            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1749            Error::<T>::TokenNotFound1750        );1751        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1752        let rft_balance = token1753            .owner1754            .iter()1755            .filter(|&i| i.owner == *owner)1756            .next()1757            .unwrap();1758        Self::remove_token_index(collection_id, item_id, owner)?;17591760        // update balance1761        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1762            .checked_sub(rft_balance.fraction)1763            .ok_or(Error::<T>::NumOverflow)?;1764        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);17651766        // Re-create owners list with sender removed1767        let index = token1768            .owner1769            .iter()1770            .position(|i| i.owner == *owner)1771            .unwrap();1772        token.owner.remove(index);1773        let owner_count = token.owner.len();17741775        // Burn the token completely if this was the last (only) owner1776        if owner_count == 0 {1777            <ReFungibleItemList<T>>::remove(collection_id, item_id);1778            <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1779        }1780        else {1781            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1782        }17831784        Ok(())1785    }17861787    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1788        ensure!(1789            <NftItemList<T>>::contains_key(collection_id, item_id),1790            Error::<T>::TokenNotFound1791        );1792        let item = <NftItemList<T>>::get(collection_id, item_id);1793        Self::remove_token_index(collection_id, item_id, &item.owner)?;17941795        // update balance1796        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1797            .checked_sub(1)1798            .ok_or(Error::<T>::NumOverflow)?;1799        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1800        <NftItemList<T>>::remove(collection_id, item_id);1801        <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18021803        Ok(())1804    }18051806    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1807        ensure!(1808            <FungibleItemList<T>>::contains_key(collection_id, owner),1809            Error::<T>::TokenNotFound1810        );1811        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1812        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18131814        // update balance1815        let new_balance = <Balance<T>>::get(collection_id, owner)1816            .checked_sub(value)1817            .ok_or(Error::<T>::NumOverflow)?;1818        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18191820        if balance.value - value > 0 {1821            balance.value -= value;1822            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1823        }1824        else {1825            <FungibleItemList<T>>::remove(collection_id, owner);1826        }18271828        Ok(())1829    }18301831    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1832        ensure!(1833            <Collection<T>>::contains_key(collection_id),1834            Error::<T>::CollectionNotFound1835        );1836        Ok(())1837    }18381839    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1840        Self::collection_exists(collection_id)?;18411842        let target_collection = <Collection<T>>::get(collection_id).unwrap();1843        ensure!(1844            subject == target_collection.owner,1845            Error::<T>::NoPermission1846        );18471848        Ok(())1849    }18501851    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1852        let target_collection = <Collection<T>>::get(collection_id).unwrap();1853        let mut result: bool = subject == target_collection.owner;1854        let exists = <AdminList<T>>::contains_key(collection_id);18551856        if !result & exists {1857            if <AdminList<T>>::get(collection_id).contains(&subject) {1858                result = true1859            }1860        }18611862        result1863    }18641865    fn check_owner_or_admin_permissions(1866        collection_id: CollectionId,1867        subject: T::AccountId,1868    ) -> DispatchResult {1869        Self::collection_exists(collection_id)?;1870        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18711872        ensure!(1873            result,1874            Error::<T>::NoPermission1875        );1876        Ok(())1877    }18781879    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1880        let target_collection = <Collection<T>>::get(collection_id).unwrap();18811882        match target_collection.mode {1883            CollectionMode::NFT => {1884                <NftItemList<T>>::get(collection_id, item_id).owner == subject1885            }1886            CollectionMode::Fungible(_) => {1887                <FungibleItemList<T>>::contains_key(collection_id, &subject)1888            }1889            CollectionMode::ReFungible => {1890                <ReFungibleItemList<T>>::get(collection_id, item_id)1891                    .owner1892                    .iter()1893                    .any(|i| i.owner == subject)1894            }1895            CollectionMode::Invalid => false,1896        }1897    }18981899    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1900        let mes = Error::<T>::AddresNotInWhiteList;1901        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);19021903        Ok(())1904    }19051906    /// Check if token exists. In case of Fungible, check if there is an entry for 1907    /// the owner in fungible balances double map1908    fn token_exists(1909        collection_id: CollectionId,1910        item_id: TokenId,1911        owner: &T::AccountId1912    ) -> DispatchResult {1913        let target_collection = <Collection<T>>::get(collection_id).unwrap();1914        let exists = match target_collection.mode1915        {1916            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1917            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),1918            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1919            _ => false1920        };19211922        ensure!(exists == true, Error::<T>::TokenNotFound);1923        Ok(())1924    }19251926    fn transfer_fungible(1927        collection_id: CollectionId,1928        value: u128,1929        owner: &T::AccountId,1930        recipient: &T::AccountId,1931    ) -> DispatchResult {1932        Self::token_exists(collection_id, 0, owner)?;19331934        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1935        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19361937        // Send balance to recipient (updates balanceOf of recipient)1938        Self::add_fungible_item(collection_id, recipient, value)?;19391940        // update balanceOf of sender1941        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);19421943        // Reduce or remove sender1944        if balance.value == value {1945            <FungibleItemList<T>>::remove(collection_id, owner);1946        }1947        else {1948            balance.value -= value;1949            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1950        }19511952        Ok(())1953    }19541955    fn transfer_refungible(1956        collection_id: CollectionId,1957        item_id: TokenId,1958        value: u128,1959        owner: T::AccountId,1960        new_owner: T::AccountId,1961    ) -> DispatchResult {1962        Self::token_exists(collection_id, item_id, &owner)?;19631964        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1965        let item = full_item1966            .owner1967            .iter()1968            .filter(|i| i.owner == owner)1969            .next()1970            .ok_or(Error::<T>::NumOverflow)?;1971        let amount = item.fraction;19721973        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19741975        // update balance1976        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1977            .checked_sub(value)1978            .ok_or(Error::<T>::NumOverflow)?;1979        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19801981        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1982            .checked_add(value)1983            .ok_or(Error::<T>::NumOverflow)?;1984        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19851986        let old_owner = item.owner.clone();1987        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19881989        // transfer1990        if amount == value && !new_owner_has_account {1991            // change owner1992            // new owner do not have account1993            let mut new_full_item = full_item.clone();1994            new_full_item1995                .owner1996                .iter_mut()1997                .find(|i| i.owner == owner)1998                .unwrap()1999                .owner = new_owner.clone();2000            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20012002            // update index collection2003            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2004        } else {2005            let mut new_full_item = full_item.clone();2006            new_full_item2007                .owner2008                .iter_mut()2009                .find(|i| i.owner == owner)2010                .unwrap()2011                .fraction -= value;20122013            // separate amount2014            if new_owner_has_account {2015                // new owner has account2016                new_full_item2017                    .owner2018                    .iter_mut()2019                    .find(|i| i.owner == new_owner)2020                    .unwrap()2021                    .fraction += value;2022            } else {2023                // new owner do not have account2024                new_full_item.owner.push(Ownership {2025                    owner: new_owner.clone(),2026                    fraction: value,2027                });2028                Self::add_token_index(collection_id, item_id, &new_owner)?;2029            }20302031            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2032        }20332034        Ok(())2035    }20362037    fn transfer_nft(2038        collection_id: CollectionId,2039        item_id: TokenId,2040        sender: T::AccountId,2041        new_owner: T::AccountId,2042    ) -> DispatchResult {2043        Self::token_exists(collection_id, item_id, &sender)?;20442045        let mut item = <NftItemList<T>>::get(collection_id, item_id);20462047        ensure!(2048            sender == item.owner,2049            Error::<T>::MustBeTokenOwner2050        );20512052        // update balance2053        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2054            .checked_sub(1)2055            .ok_or(Error::<T>::NumOverflow)?;2056        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20572058        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2059            .checked_add(1)2060            .ok_or(Error::<T>::NumOverflow)?;2061        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20622063        // change owner2064        let old_owner = item.owner.clone();2065        item.owner = new_owner.clone();2066        <NftItemList<T>>::insert(collection_id, item_id, item);20672068        // update index collection2069        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;20702071        Ok(())2072    }2073    2074    fn set_re_fungible_variable_data(2075        collection_id: CollectionId,2076        item_id: TokenId,2077        data: Vec<u8>2078    ) -> DispatchResult {2079        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20802081        item.variable_data = data;20822083        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20842085        Ok(())2086    }20872088    fn set_nft_variable_data(2089        collection_id: CollectionId,2090        item_id: TokenId,2091        data: Vec<u8>2092    ) -> DispatchResult {2093        let mut item = <NftItemList<T>>::get(collection_id, item_id);2094        2095        item.variable_data = data;20962097        <NftItemList<T>>::insert(collection_id, item_id, item);2098        2099        Ok(())2100    }21012102    fn init_collection(item: &CollectionType<T>) {2103        // check params2104        assert!(2105            item.decimal_points <= MAX_DECIMAL_POINTS,2106            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2107        );2108        assert!(2109            item.name.len() <= 64,2110            "Collection name can not be longer than 63 char"2111        );2112        assert!(2113            item.name.len() <= 256,2114            "Collection description can not be longer than 255 char"2115        );2116        assert!(2117            item.token_prefix.len() <= 16,2118            "Token prefix can not be longer than 15 char"2119        );21202121        // Generate next collection ID2122        let next_id = CreatedCollectionCount::get()2123            .checked_add(1)2124            .unwrap();21252126        CreatedCollectionCount::put(next_id);2127    }21282129    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2130        let current_index = <ItemListIndex>::get(collection_id)2131            .checked_add(1)2132            .unwrap();21332134        let item_owner = item.owner.clone();2135        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21362137        <ItemListIndex>::insert(collection_id, current_index);21382139        // Update balance2140        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2141            .checked_add(1)2142            .unwrap();2143        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2144    }21452146    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2147        let current_index = <ItemListIndex>::get(collection_id)2148            .checked_add(1)2149            .unwrap();21502151        Self::add_token_index(collection_id, current_index, owner).unwrap();21522153        <ItemListIndex>::insert(collection_id, current_index);21542155        // Update balance2156        let new_balance = <Balance<T>>::get(collection_id, owner)2157            .checked_add(item.value)2158            .unwrap();2159        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2160    }21612162    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2163        let current_index = <ItemListIndex>::get(collection_id)2164            .checked_add(1)2165            .unwrap();21662167        let value = item.owner.first().unwrap().fraction;2168        let owner = item.owner.first().unwrap().owner.clone();21692170        Self::add_token_index(collection_id, current_index, &owner).unwrap();21712172        <ItemListIndex>::insert(collection_id, current_index);21732174        // Update balance2175        let new_balance = <Balance<T>>::get(collection_id, &owner)2176            .checked_add(value)2177            .unwrap();2178        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2179    }21802181    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {21822183        // add to account limit2184        if <AccountItemCount<T>>::contains_key(owner) {21852186            // bound Owned tokens by a single address2187            let count = <AccountItemCount<T>>::get(owner);2188            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21892190            <AccountItemCount<T>>::insert(owner.clone(), count2191                .checked_add(1)2192                .ok_or(Error::<T>::NumOverflow)?);2193        }2194        else {2195            <AccountItemCount<T>>::insert(owner.clone(), 1);2196        }21972198        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2199        if list_exists {2200            let mut list = <AddressTokens<T>>::get(collection_id, owner);2201            let item_contains = list.contains(&item_index.clone());22022203            if !item_contains {2204                list.push(item_index.clone());2205            }22062207            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2208        } else {2209            let mut itm = Vec::new();2210            itm.push(item_index.clone());2211            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2212        }22132214        Ok(())2215    }22162217    fn remove_token_index(2218        collection_id: CollectionId,2219        item_index: TokenId,2220        owner: &T::AccountId,2221    ) -> DispatchResult {22222223        // update counter2224        <AccountItemCount<T>>::insert(owner.clone(), 2225            <AccountItemCount<T>>::get(owner)2226            .checked_sub(1)2227            .ok_or(Error::<T>::NumOverflow)?);222822292230        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2231        if list_exists {2232            let mut list = <AddressTokens<T>>::get(collection_id, owner);2233            let item_contains = list.contains(&item_index.clone());22342235            if item_contains {2236                list.retain(|&item| item != item_index);2237                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2238            }2239        }22402241        Ok(())2242    }22432244    fn move_token_index(2245        collection_id: CollectionId,2246        item_index: TokenId,2247        old_owner: &T::AccountId,2248        new_owner: &T::AccountId,2249    ) -> DispatchResult {2250        Self::remove_token_index(collection_id, item_index, old_owner)?;2251        Self::add_token_index(collection_id, item_index, new_owner)?;22522253        Ok(())2254    }2255    2256    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2257        if <ContractOwner<T>>::contains_key(contract.clone()) {2258            let owner = <ContractOwner<T>>::get(contract);2259            ensure!(account == owner, Error::<T>::NoPermission);2260        } else {2261            fail!(Error::<T>::NoPermission);2262        }22632264        Ok(())2265    }2266}22672268////////////////////////////////////////////////////////////////////////////////////////////////////2269// Economic models2270// #region22712272/// Fee multiplier.2273pub type Multiplier = FixedU128;22742275type BalanceOf<T> = <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::Balance;22762277/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2278/// in the queue.2279#[derive(Encode, Decode, Clone, Eq, PartialEq)]2280pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);22812282impl<T: Config + Send + Sync> sp_std::fmt::Debug 2283    for ChargeTransactionPayment<T>2284{2285	#[cfg(feature = "std")]2286	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2287		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2288	}2289	#[cfg(not(feature = "std"))]2290	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2291		Ok(())2292	}2293}22942295impl<T: Config> ChargeTransactionPayment<T>2296where2297    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2298    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2299    T::AccountId: AsRef<[u8]>,2300    T::AccountId: UncheckedFrom<T::Hash>,2301{2302    fn traditional_fee(2303        len: usize,2304        info: &DispatchInfoOf<T::Call>,2305        tip: BalanceOf<T>,2306    ) -> BalanceOf<T>2307    where2308        T::Call: Dispatchable<Info = DispatchInfo>,2309    {2310        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2311    }23122313	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2314        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2315        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2316        let len_saturation = max_block_length as u64 / (len as u64).max(1);2317        let coefficient: BalanceOf<T> = weight_saturation2318            .min(len_saturation)2319            .saturated_into::<BalanceOf<T>>();2320        final_fee2321            .saturating_mul(coefficient)2322            .saturated_into::<TransactionPriority>()2323    }23242325    fn withdraw_fee(2326        &self,2327        who: &T::AccountId,2328        call: &T::Call,2329        info: &DispatchInfoOf<T::Call>,2330        len: usize,2331	) -> Result<2332		(2333			BalanceOf<T>,2334			<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2335		),2336		TransactionValidityError,2337	> {2338        let tip = self.0;23392340        // Set fee based on call type. Creating collection costs 1 Unique.2341        // All other transactions have traditional fees so far2342        // let fee = match call.is_sub_type() {2343        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2344        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2345        //                                                 // _ => <BalanceOf<T>>::from(100)2346        // };2347        let fee = Self::traditional_fee(len, info, tip);23482349        // Only mess with balances if fee is not zero.2350        if fee.is_zero() {2351            return <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2352			.map(|i| (fee, i));2353        }23542355        // Determine who is paying transaction fee based on ecnomic model2356        // Parse call to extract collection ID and access collection sponsor2357        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2358            Some(Call::create_item(collection_id, _owner, _properties)) => {2359                let collection = <Collection<T>>::get(collection_id).unwrap();23602361                // sponsor timeout2362                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;23632364                let limit = collection.limits.sponsor_transfer_timeout;2365                let mut sponsored = true;2366                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2367                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2368                    let limit_time = last_tx_block + limit.into();2369                    if block_number <= limit_time {2370                        sponsored = false;2371                    }2372                }2373                if sponsored {2374                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2375                }23762377                // check free create limit2378                if (collection.limits.sponsored_data_size >= (_properties.len() as u32)) &&2379                   (collection.sponsor_confirmed) &&2380                   (sponsored)2381                {2382                    collection.sponsor2383                } else {2384                    T::AccountId::default()2385                }2386            }2387            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2388                let collection = <Collection<T>>::get(collection_id).unwrap();2389                2390                let mut sponsor_transfer = false;2391                if collection.sponsor_confirmed {23922393                    let collection_limits = collection.limits;2394                    let collection_mode = collection.mode;2395    2396                    // sponsor timeout2397                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2398                    sponsor_transfer = match collection_mode {2399                        CollectionMode::NFT => {2400    2401                            // get correct limit2402                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2403                                collection_limits.sponsor_transfer_timeout2404                            } else {2405                                ChainLimit::get().nft_sponsor_transfer_timeout2406                            };2407    2408                            let mut sponsored = true;2409                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2410                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2411                                let limit_time = last_tx_block + limit.into();2412                                if block_number <= limit_time {2413                                    sponsored = false;2414                                }2415                            }2416                            if sponsored {2417                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2418                            }24192420                            sponsored2421                        }2422                        CollectionMode::Fungible(_) => {2423    2424                            // get correct limit2425                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2426                                collection_limits.sponsor_transfer_timeout2427                            } else {2428                                ChainLimit::get().fungible_sponsor_transfer_timeout2429                            };2430    2431                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2432                            let mut sponsored = true;2433                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2434                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2435                                let limit_time = last_tx_block + limit.into();2436                                if block_number <= limit_time {2437                                    sponsored = false;2438                                }2439                            }2440                            if sponsored {2441                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2442                            }24432444                            sponsored2445                        }2446                        CollectionMode::ReFungible => {2447    2448                            // get correct limit2449                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2450                                collection_limits.sponsor_transfer_timeout2451                            } else {2452                                ChainLimit::get().refungible_sponsor_transfer_timeout2453                            };2454    2455                            let mut sponsored = true;2456                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2457                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2458                                let limit_time = last_tx_block + limit.into();2459                                if block_number <= limit_time {2460                                    sponsored = false;2461                                }2462                            }2463                            if sponsored {2464                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2465                            }24662467                            sponsored2468                        }2469                        _ => {2470                            false2471                        },2472                    };2473                }24742475                if !sponsor_transfer {2476                    T::AccountId::default()2477                } else {2478                    collection.sponsor2479                }2480            }24812482            Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2483                let mut sponsor_metadata_changes = false;24842485                let collection = <Collection<T>>::get(collection_id).unwrap();24862487                if2488                    collection.sponsor_confirmed &&2489                    // Can't sponsor fungible collection, this tx will be rejected2490                    // as invalid2491                    !matches!(collection.mode, CollectionMode::Fungible(_)) &&2492                    data.len() <= collection.limits.sponsored_data_size as usize2493                {2494                    if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2495                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;24962497                        if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2498                            .map(|last_block| block_number - last_block > rate_limit)2499                            .unwrap_or(true) 2500                        {2501                            sponsor_metadata_changes = true;2502                            <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2503                        }2504                    }2505                }25062507                if !sponsor_metadata_changes {2508                    T::AccountId::default()2509                } else {2510                    collection.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