git.delta.rocks / unique-network / refs/commits / 05b679abd400

difftreelog

source

pallets/nft/src/lib.rs97.8 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, Default, Debug, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub struct CollectionType<AccountId> {129    pub owner: AccountId,130    pub mode: CollectionMode,131    pub access: AccessMode,132    pub decimal_points: DecimalPoints,133    pub name: Vec<u16>,        // 64 include null escape char134    pub description: Vec<u16>, // 256 include null escape char135    pub token_prefix: Vec<u8>, // 16 include null escape char136    pub mint_mode: bool,137    pub offchain_schema: Vec<u8>,138    pub schema_version: SchemaVersion,139    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender140    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.141    pub limits: CollectionLimits, // Collection private restrictions 142    pub variable_on_chain_schema: Vec<u8>, //143    pub const_on_chain_schema: Vec<u8>, //144}145146#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]147#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]148pub struct NftItemType<AccountId> {149    pub owner: AccountId,150    pub const_data: Vec<u8>,151    pub variable_data: Vec<u8>,152}153154#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]155#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]156pub struct FungibleItemType {157    pub value: u128,158}159160#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]161#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]162pub struct ReFungibleItemType<AccountId> {163    pub owner: Vec<Ownership<AccountId>>,164    pub const_data: Vec<u8>,165    pub variable_data: Vec<u8>,166}167168// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]169// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170// pub struct VestingItem<AccountId, Moment> {171//     pub sender: AccountId,172//     pub recipient: AccountId,173//     pub collection_id: CollectionId,174//     pub item_id: TokenId,175//     pub amount: u64,176//     pub vesting_date: Moment,177// }178179#[derive(Encode, Decode, Debug, Clone, PartialEq)]180#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]181pub struct CollectionLimits {182    pub account_token_ownership_limit: u32,183    pub sponsored_data_size: u32,184    pub token_limit: u32,185186    // Timeouts for item types in passed blocks187    pub sponsor_transfer_timeout: u32,188    pub owner_can_transfer: bool,189    pub owner_can_destroy: bool,190}191192impl Default for CollectionLimits {193    fn default() -> CollectionLimits {194        CollectionLimits { 195            account_token_ownership_limit: 10_000_000, 196            token_limit: u32::max_value(),197            sponsored_data_size: u32::max_value(), 198            sponsor_transfer_timeout: 14400,199            owner_can_transfer: true,200            owner_can_destroy: true201        }202    }203}204205#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]206#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]207pub struct ChainLimits {208    pub collection_numbers_limit: u32,209    pub account_token_ownership_limit: u32,210    pub collections_admins_limit: u64,211    pub custom_data_limit: u32,212213    // Timeouts for item types in passed blocks214    pub nft_sponsor_transfer_timeout: u32,215    pub fungible_sponsor_transfer_timeout: u32,216    pub refungible_sponsor_transfer_timeout: u32,217218    // Schema limits219    pub offchain_schema_limit: u32,220    pub variable_on_chain_schema_limit: u32,221    pub const_on_chain_schema_limit: u32,222}223224pub trait WeightInfo {225	fn create_collection() -> Weight;226	fn destroy_collection() -> Weight;227	fn add_to_white_list() -> Weight;228	fn remove_from_white_list() -> Weight;229    fn set_public_access_mode() -> Weight;230    fn set_mint_permission() -> Weight;231    fn change_collection_owner() -> Weight;232    fn add_collection_admin() -> Weight;233    fn remove_collection_admin() -> Weight;234    fn set_collection_sponsor() -> Weight;235    fn confirm_sponsorship() -> Weight;236    fn remove_collection_sponsor() -> Weight;237    fn create_item(s: usize) -> Weight;238    fn burn_item() -> Weight;239    fn transfer() -> Weight;240    fn approve() -> Weight;241    fn transfer_from() -> Weight;242    fn set_offchain_schema() -> Weight;243    fn set_const_on_chain_schema() -> Weight;244    fn set_variable_on_chain_schema() -> Weight;245    fn set_variable_meta_data() -> Weight;246    fn enable_contract_sponsoring() -> Weight;247    fn set_schema_version() -> Weight;248    fn set_chain_limits() -> Weight;249    fn set_contract_sponsoring_rate_limit() -> Weight;250    fn toggle_contract_white_list() -> Weight;251    fn add_to_contract_white_list() -> Weight;252    fn remove_from_contract_white_list() -> Weight;253    fn set_collection_limits() -> Weight;254}255256#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]257#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]258pub struct CreateNftData {259    pub const_data: Vec<u8>,260    pub variable_data: Vec<u8>,261}262263#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]264#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]265pub struct CreateFungibleData {266    pub value: u128,267}268269#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]270#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]271pub struct CreateReFungibleData {272    pub const_data: Vec<u8>,273    pub variable_data: Vec<u8>,274    pub pieces: u128,275}276277#[derive(Encode, Decode, Debug, Clone, PartialEq)]278#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]279pub enum CreateItemData {280    NFT(CreateNftData),281    Fungible(CreateFungibleData),282    ReFungible(CreateReFungibleData),283}284285impl CreateItemData {286    pub fn len(&self) -> usize {287        let len = match self {288            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),289            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),290            _ => 0291        };292        293        return len;294    }295}296297impl From<CreateNftData> for CreateItemData {298    fn from(item: CreateNftData) -> Self {299        CreateItemData::NFT(item)300    }301}302303impl From<CreateReFungibleData> for CreateItemData {304    fn from(item: CreateReFungibleData) -> Self {305        CreateItemData::ReFungible(item)306    }307}308309impl From<CreateFungibleData> for CreateItemData {310    fn from(item: CreateFungibleData) -> Self {311        CreateItemData::Fungible(item)312    }313}314315316decl_error! {317	/// Error for non-fungible-token module.318	pub enum Error for Module<T: Config> {319        /// Total collections bound exceeded.320        TotalCollectionsLimitExceeded,321		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.322        CollectionDecimalPointLimitExceeded, 323        /// Collection name can not be longer than 63 char.324        CollectionNameLimitExceeded, 325        /// Collection description can not be longer than 255 char.326        CollectionDescriptionLimitExceeded, 327        /// Token prefix can not be longer than 15 char.328        CollectionTokenPrefixLimitExceeded,329        /// This collection does not exist.330        CollectionNotFound,331        /// Item not exists.332        TokenNotFound,333        /// Admin not found334        AdminNotFound,335        /// Arithmetic calculation overflow.336        NumOverflow,       337        /// Account already has admin role.338        AlreadyAdmin,  339        /// You do not own this collection.340        NoPermission,341        /// This address is not set as sponsor, use setCollectionSponsor first.342        ConfirmUnsetSponsorFail,343        /// Collection is not in mint mode.344        PublicMintingNotAllowed,345        /// Sender parameter and item owner must be equal.346        MustBeTokenOwner,347        /// Item balance not enough.348        TokenValueTooLow,349        /// Size of item is too large.350        NftSizeLimitExceeded,351        /// No approve found352        ApproveNotFound,353        /// Requested value more than approved.354        TokenValueNotEnough,355        /// Only approved addresses can call this method.356        ApproveRequired,357        /// Address is not in white list.358        AddresNotInWhiteList,359        /// Number of collection admins bound exceeded.360        CollectionAdminsLimitExceeded,361        /// Owned tokens by a single address bound exceeded.362        AddressOwnershipLimitExceeded,363        /// Length of items properties must be greater than 0.364        EmptyArgument,365        /// const_data exceeded data limit.366        TokenConstDataLimitExceeded,367        /// variable_data exceeded data limit.368        TokenVariableDataLimitExceeded,369        /// Not NFT item data used to mint in NFT collection.370        NotNftDataUsedToMintNftCollectionToken,371        /// Not Fungible item data used to mint in Fungible collection.372        NotFungibleDataUsedToMintFungibleCollectionToken,373        /// Not Re Fungible item data used to mint in Re Fungible collection.374        NotReFungibleDataUsedToMintReFungibleCollectionToken,375        /// Unexpected collection type.376        UnexpectedCollectionType,377        /// Can't store metadata in fungible tokens.378        CantStoreMetadataInFungibleTokens,379        /// Collection token limit exceeded380        CollectionTokenLimitExceeded,381        /// Account token limit exceeded per collection382        AccountTokenLimitExceeded,383        /// Collection limit bounds per collection exceeded384        CollectionLimitBoundsExceeded,385        /// Tried to enable permissions which are only permitted to be disabled386        OwnerPermissionsCantBeReverted,387        /// Schema data size limit bound exceeded388        SchemaDataLimitExceeded,389        /// Maximum refungibility exceeded390        WrongRefungiblePieces391	}392}393394pub trait Config: system::Config + Sized + transaction_payment::Config + pallet_contracts::Config {395    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;396397    /// Weight information for extrinsics in this pallet.398	type WeightInfo: WeightInfo;399}400401#[cfg(feature = "runtime-benchmarks")]402mod benchmarking;403404// #endregion405406decl_storage! {407    trait Store for Module<T: Config> as Nft {408409        // Private members410        NextCollectionID: CollectionId;411        CreatedCollectionCount: u32;412        ChainVersion: u64;413        ItemListIndex: map hasher(identity) CollectionId => TokenId;414415        // Chain limits struct416        pub ChainLimit get(fn chain_limit) config(): ChainLimits;417418        // Bound counters419        CollectionCount: u32;420        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;421422        // Basic collections423        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;424        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;425        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;426427        /// Balance owner per collection map428        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;429430        /// second parameter: item id + owner account id + spender account id431        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;432433        /// Item collections434        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;435        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;436        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;437438        /// Index list439        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;440441        /// Tokens transfer baskets442        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;443        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;444        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;445        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;446447        // Contract Sponsorship and Ownership448        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;449        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;450        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;451        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;452        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 453        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 454    }455    add_extra_genesis {456        build(|config: &GenesisConfig<T>| {457            // Modification of storage458            for (_num, _c) in &config.collection {459                <Module<T>>::init_collection(_c);460            }461462            for (_num, _c, _i) in &config.nft_item_id {463                <Module<T>>::init_nft_token(*_c, _i);464            }465466            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {467                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);468            }469470            for (_num, _c, _i) in &config.refungible_item_id {471                <Module<T>>::init_refungible_token(*_c, _i);472            }473        })474    }475}476477decl_event!(478    pub enum Event<T>479    where480        AccountId = <T as system::Config>::AccountId,481    {482        /// New collection was created483        /// 484        /// # Arguments485        /// 486        /// * collection_id: Globally unique identifier of newly created collection.487        /// 488        /// * mode: [CollectionMode] converted into u8.489        /// 490        /// * account_id: Collection owner.491        Created(CollectionId, u8, AccountId),492493        /// New item was created.494        /// 495        /// # Arguments496        /// 497        /// * collection_id: Id of the collection where item was created.498        /// 499        /// * item_id: Id of an item. Unique within the collection.500        ///501        /// * recipient: Owner of newly created item 502        ItemCreated(CollectionId, TokenId, AccountId),503504        /// Collection item was burned.505        /// 506        /// # Arguments507        /// 508        /// collection_id.509        /// 510        /// item_id: Identifier of burned NFT.511        ItemDestroyed(CollectionId, TokenId),512513        /// Item was transferred514        ///515        /// * collection_id: Id of collection to which item is belong516        ///517        /// * item_id: Id of an item518        ///519        /// * sender: Original owner of item520        ///521        /// * recipient: New owner of item522        ///523        /// * amount: Always 1 for NFT524        Transfer(CollectionId, TokenId, AccountId, AccountId, u128),525    }526);527528decl_module! {529    pub struct Module<T: Config> for enum Call 530    where 531        origin: T::Origin532    {533        fn deposit_event() = default;534        type Error = Error<T>;535536        fn on_initialize(now: T::BlockNumber) -> Weight {537538            if ChainVersion::get() < 2539            {540                let value = NextCollectionID::get();541                CreatedCollectionCount::put(value);542                ChainVersion::put(2);543            }544545            0546        }547548        /// 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.549        /// 550        /// # Permissions551        /// 552        /// * Anyone.553        /// 554        /// # Arguments555        /// 556        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.557        /// 558        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.559        /// 560        /// * token_prefix: UTF-8 string with token prefix.561        /// 562        /// * mode: [CollectionMode] collection type and type dependent data.563        // returns collection ID564        #[weight = <T as Config>::WeightInfo::create_collection()]565        pub fn create_collection(origin,566                                 collection_name: Vec<u16>,567                                 collection_description: Vec<u16>,568                                 token_prefix: Vec<u8>,569                                 mode: CollectionMode) -> DispatchResult {570571            // Anyone can create a collection572            let who = ensure_signed(origin)?;573574            let decimal_points = match mode {575                CollectionMode::Fungible(points) => points,576                _ => 0577            };578579            // bound Total number of collections580            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);581582            // check params583            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);584            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);585            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);586            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);587588            // Generate next collection ID589            let next_id = CreatedCollectionCount::get()590                .checked_add(1)591                .ok_or(Error::<T>::NumOverflow)?;592593            // bound counter594            let total = CollectionCount::get()595                .checked_add(1)596                .ok_or(Error::<T>::NumOverflow)?;597598            CreatedCollectionCount::put(next_id);599            CollectionCount::put(total);600601            // Create new collection602            let new_collection = CollectionType {603                owner: who.clone(),604                name: collection_name,605                mode: mode.clone(),606                mint_mode: false,607                access: AccessMode::Normal,608                description: collection_description,609                decimal_points: decimal_points,610                token_prefix: token_prefix,611                offchain_schema: Vec::new(),612                schema_version: SchemaVersion::ImageURL,613                sponsor: T::AccountId::default(),614                sponsor_confirmed: false,615                variable_on_chain_schema: Vec::new(),616                const_on_chain_schema: Vec::new(),617                limits: CollectionLimits::default()618            };619620            // Add new collection to map621            <Collection<T>>::insert(next_id, new_collection);622623            // call event624            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));625626            Ok(())627        }628629        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.630        /// 631        /// # Permissions632        /// 633        /// * Collection Owner.634        /// 635        /// # Arguments636        /// 637        /// * collection_id: collection to destroy.638        #[weight = <T as Config>::WeightInfo::destroy_collection()]639        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {640641            let sender = ensure_signed(origin)?;642            Self::check_owner_permissions(collection_id, sender)?;643644            let target_collection = <Collection<T>>::get(collection_id);645            if !target_collection.limits.owner_can_destroy {646                fail!(Error::<T>::NoPermission);647            }648649            <AddressTokens<T>>::remove_prefix(collection_id);650            <Allowances<T>>::remove_prefix(collection_id);651            <Balance<T>>::remove_prefix(collection_id);652            <ItemListIndex>::remove(collection_id);653            <AdminList<T>>::remove(collection_id);654            <Collection<T>>::remove(collection_id);655            <WhiteList<T>>::remove_prefix(collection_id);656657            <NftItemList<T>>::remove_prefix(collection_id);658            <FungibleItemList<T>>::remove_prefix(collection_id);659            <ReFungibleItemList<T>>::remove_prefix(collection_id);660661            <NftTransferBasket<T>>::remove_prefix(collection_id);662            <FungibleTransferBasket<T>>::remove_prefix(collection_id);663            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);664665            if CollectionCount::get() > 0666            {667                // bound couter668                let total = CollectionCount::get()669                    .checked_sub(1)670                    .ok_or(Error::<T>::NumOverflow)?;671672                CollectionCount::put(total);673            }674675            Ok(())676        }677678        /// Add an address to white list.679        /// 680        /// # Permissions681        /// 682        /// * Collection Owner683        /// * Collection Admin684        /// 685        /// # Arguments686        /// 687        /// * collection_id.688        /// 689        /// * address.690        #[weight = <T as Config>::WeightInfo::add_to_white_list()]691        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{692693            let sender = ensure_signed(origin)?;694            Self::check_owner_or_admin_permissions(collection_id, sender)?;695696            <WhiteList<T>>::insert(collection_id, address, true);697            698            Ok(())699        }700701        /// Remove an address from white list.702        /// 703        /// # Permissions704        /// 705        /// * Collection Owner706        /// * Collection Admin707        /// 708        /// # Arguments709        /// 710        /// * collection_id.711        /// 712        /// * address.713        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]714        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{715716            let sender = ensure_signed(origin)?;717            Self::check_owner_or_admin_permissions(collection_id, sender)?;718719            <WhiteList<T>>::remove(collection_id, address);720721            Ok(())722        }723724        /// Toggle between normal and white list access for the methods with access for `Anyone`.725        /// 726        /// # Permissions727        /// 728        /// * Collection Owner.729        /// 730        /// # Arguments731        /// 732        /// * collection_id.733        /// 734        /// * mode: [AccessMode]735        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]736        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult737        {738            let sender = ensure_signed(origin)?;739740            Self::check_owner_permissions(collection_id, sender)?;741            let mut target_collection = <Collection<T>>::get(collection_id);742            target_collection.access = mode;743            <Collection<T>>::insert(collection_id, target_collection);744745            Ok(())746        }747748        /// Allows Anyone to create tokens if:749        /// * White List is enabled, and750        /// * Address is added to white list, and751        /// * This method was called with True parameter752        /// 753        /// # Permissions754        /// * Collection Owner755        ///756        /// # Arguments757        /// 758        /// * collection_id.759        /// 760        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.761        #[weight = <T as Config>::WeightInfo::set_mint_permission()]762        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult763        {764            let sender = ensure_signed(origin)?;765766            Self::check_owner_permissions(collection_id, sender)?;767            let mut target_collection = <Collection<T>>::get(collection_id);768            target_collection.mint_mode = mint_permission;769            <Collection<T>>::insert(collection_id, target_collection);770771            Ok(())772        }773774        /// Change the owner of the collection.775        /// 776        /// # Permissions777        /// 778        /// * Collection Owner.779        /// 780        /// # Arguments781        /// 782        /// * collection_id.783        /// 784        /// * new_owner.785        #[weight = <T as Config>::WeightInfo::change_collection_owner()]786        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {787788            let sender = ensure_signed(origin)?;789            Self::check_owner_permissions(collection_id, sender)?;790            let mut target_collection = <Collection<T>>::get(collection_id);791            target_collection.owner = new_owner;792            <Collection<T>>::insert(collection_id, target_collection);793794            Ok(())795        }796797        /// Adds an admin of the Collection.798        /// 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. 799        /// 800        /// # Permissions801        /// 802        /// * Collection Owner.803        /// * Collection Admin.804        /// 805        /// # Arguments806        /// 807        /// * collection_id: ID of the Collection to add admin for.808        /// 809        /// * new_admin_id: Address of new admin to add.810        #[weight = <T as Config>::WeightInfo::add_collection_admin()]811        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {812813            let sender = ensure_signed(origin)?;814            Self::check_owner_or_admin_permissions(collection_id, sender)?;815            let mut admin_arr: Vec<T::AccountId> = Vec::new();816817            if <AdminList<T>>::contains_key(collection_id)818            {819                admin_arr = <AdminList<T>>::get(collection_id);820                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);821            }822823            // Number of collection admins824            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);825826            admin_arr.push(new_admin_id);827            <AdminList<T>>::insert(collection_id, admin_arr);828829            Ok(())830        }831832        /// 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.833        ///834        /// # Permissions835        /// 836        /// * Collection Owner.837        /// * Collection Admin.838        /// 839        /// # Arguments840        /// 841        /// * collection_id: ID of the Collection to remove admin for.842        /// 843        /// * account_id: Address of admin to remove.844        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]845        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {846847            let sender = ensure_signed(origin)?;848            Self::check_owner_or_admin_permissions(collection_id, sender)?;849            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);850851            let mut admin_arr = <AdminList<T>>::get(collection_id);852            admin_arr.retain(|i| *i != account_id);853            <AdminList<T>>::insert(collection_id, admin_arr);854855            Ok(())856        }857858        /// # Permissions859        /// 860        /// * Collection Owner861        /// 862        /// # Arguments863        /// 864        /// * collection_id.865        /// 866        /// * new_sponsor.867        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]868        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {869870            let sender = ensure_signed(origin)?;871            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);872873            let mut target_collection = <Collection<T>>::get(collection_id);874            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);875876            target_collection.sponsor = new_sponsor;877            target_collection.sponsor_confirmed = false;878            <Collection<T>>::insert(collection_id, target_collection);879880            Ok(())881        }882883        /// # Permissions884        /// 885        /// * Sponsor.886        /// 887        /// # Arguments888        /// 889        /// * collection_id.890        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]891        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {892893            let sender = ensure_signed(origin)?;894            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);895896            let mut target_collection = <Collection<T>>::get(collection_id);897            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);898899            target_collection.sponsor_confirmed = true;900            <Collection<T>>::insert(collection_id, target_collection);901902            Ok(())903        }904905        /// Switch back to pay-per-own-transaction model.906        ///907        /// # Permissions908        ///909        /// * Collection owner.910        /// 911        /// # Arguments912        /// 913        /// * collection_id.914        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]915        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {916917            let sender = ensure_signed(origin)?;918            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);919920            let mut target_collection = <Collection<T>>::get(collection_id);921            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);922923            target_collection.sponsor = T::AccountId::default();924            target_collection.sponsor_confirmed = false;925            <Collection<T>>::insert(collection_id, target_collection);926927            Ok(())928        }929930        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.931        /// 932        /// # Permissions933        /// 934        /// * Collection Owner.935        /// * Collection Admin.936        /// * Anyone if937        ///     * White List is enabled, and938        ///     * Address is added to white list, and939        ///     * MintPermission is enabled (see SetMintPermission method)940        /// 941        /// # Arguments942        /// 943        /// * collection_id: ID of the collection.944        /// 945        /// * owner: Address, initial owner of the NFT.946        ///947        /// * data: Token data to store on chain.948        // #[weight =949        // (130_000_000 as Weight)950        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))951        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))952        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]953954        #[weight = <T as Config>::WeightInfo::create_item(data.len())]955        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {956957            let sender = ensure_signed(origin)?;958959            Self::collection_exists(collection_id)?;960961            let target_collection = <Collection<T>>::get(collection_id);962963            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;964            Self::validate_create_item_args(&target_collection, &data)?;965            Self::create_item_no_validation(collection_id, owner, data)?;966967            Ok(())968        }969970        /// This method creates multiple instances of NFT Collection created with CreateCollection method.971        /// 972        /// # Permissions973        /// 974        /// * Collection Owner.975        /// * Collection Admin.976        /// * Anyone if977        ///     * White List is enabled, and978        ///     * Address is added to white list, and979        ///     * MintPermission is enabled (see SetMintPermission method)980        /// 981        /// # Arguments982        /// 983        /// * collection_id: ID of the collection.984        /// 985        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].986        /// 987        /// * owner: Address, initial owner of the NFT.988        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()989                               .map(|data| { data.len() })990                               .sum())]991        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {992993            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);994            let sender = ensure_signed(origin)?;995996            Self::collection_exists(collection_id)?;997            let target_collection = <Collection<T>>::get(collection_id);998999            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;10001001            for data in &items_data {1002                Self::validate_create_item_args(&target_collection, data)?;1003            }1004            for data in &items_data {1005                Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?;1006            }10071008            Ok(())1009        }10101011        /// Destroys a concrete instance of NFT.1012        /// 1013        /// # Permissions1014        /// 1015        /// * Collection Owner.1016        /// * Collection Admin.1017        /// * Current NFT Owner.1018        /// 1019        /// # Arguments1020        /// 1021        /// * collection_id: ID of the collection.1022        /// 1023        /// * item_id: ID of NFT to burn.1024        #[weight = <T as Config>::WeightInfo::burn_item()]1025        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10261027            let sender = ensure_signed(origin)?;1028            Self::collection_exists(collection_id)?;10291030            // Transfer permissions check1031            let target_collection = <Collection<T>>::get(collection_id);1032            ensure!(1033                Self::is_item_owner(sender.clone(), collection_id, item_id) ||1034                (1035                    target_collection.limits.owner_can_transfer &&1036                    Self::is_owner_or_admin_permissions(collection_id, sender.clone())1037                ),1038                Error::<T>::NoPermission1039            );10401041            if target_collection.access == AccessMode::WhiteList {1042                Self::check_white_list(collection_id, &sender)?;1043            }10441045            match target_collection.mode1046            {1047                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1048                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1049                CollectionMode::ReFungible  => Self::burn_refungible_item(collection_id, item_id, &sender)?,1050                _ => ()1051            };10521053            // call event1054            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10551056            Ok(())1057        }10581059        /// Change ownership of the token.1060        /// 1061        /// # Permissions1062        /// 1063        /// * Collection Owner1064        /// * Collection Admin1065        /// * Current NFT owner1066        ///1067        /// # Arguments1068        /// 1069        /// * recipient: Address of token recipient.1070        /// 1071        /// * collection_id.1072        /// 1073        /// * item_id: ID of the item1074        ///     * Non-Fungible Mode: Required.1075        ///     * Fungible Mode: Ignored.1076        ///     * Re-Fungible Mode: Required.1077        /// 1078        /// * value: Amount to transfer.1079        ///     * Non-Fungible Mode: Ignored1080        ///     * Fungible Mode: Must specify transferred amount1081        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1082        #[weight = <T as Config>::WeightInfo::transfer()]1083        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1084            let sender = ensure_signed(origin)?;1085            Self::transfer_internal(sender, recipient, collection_id, item_id, value)1086        }10871088        /// Set, change, or remove approved address to transfer the ownership of the NFT.1089        /// 1090        /// # Permissions1091        /// 1092        /// * Collection Owner1093        /// * Collection Admin1094        /// * Current NFT owner1095        /// 1096        /// # Arguments1097        /// 1098        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1099        /// 1100        /// * collection_id.1101        /// 1102        /// * item_id: ID of the item.1103        #[weight = <T as Config>::WeightInfo::approve()]1104        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {11051106            let sender = ensure_signed(origin)?;11071108            Self::collection_exists(collection_id)?;1109            Self::token_exists(collection_id, item_id, &sender)?;11101111            // Transfer permissions check1112            let target_collection = <Collection<T>>::get(collection_id);1113            ensure!(1114                Self::is_item_owner(sender.clone(), collection_id, item_id) ||1115                (1116                    target_collection.limits.owner_can_transfer &&1117                    Self::is_owner_or_admin_permissions(collection_id, sender.clone())1118                ),1119                Error::<T>::NoPermission1120            );11211122            if target_collection.access == AccessMode::WhiteList {1123                Self::check_white_list(collection_id, &sender)?;1124                Self::check_white_list(collection_id, &spender)?;1125            }11261127            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1128            let mut allowance: u128 = amount;1129            if allowance_exists {1130                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1131            }1132            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11331134            Ok(())1135        }1136        1137        /// 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.1138        /// 1139        /// # Permissions1140        /// * Collection Owner1141        /// * Collection Admin1142        /// * Current NFT owner1143        /// * Address approved by current NFT owner1144        /// 1145        /// # Arguments1146        /// 1147        /// * from: Address that owns token.1148        /// 1149        /// * recipient: Address of token recipient.1150        /// 1151        /// * collection_id.1152        /// 1153        /// * item_id: ID of the item.1154        /// 1155        /// * value: Amount to transfer.1156        #[weight = <T as Config>::WeightInfo::transfer_from()]1157        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11581159            let sender = ensure_signed(origin)?;1160            let mut appoved_transfer = false;11611162            // Check approval1163            let mut approval: u128 = 0;1164            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1165                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1166                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1167                appoved_transfer = true;1168            }11691170            let target_collection = <Collection<T>>::get(collection_id);11711172            // Limits check1173            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11741175            // Transfer permissions check         1176            ensure!(1177                appoved_transfer || 1178                (1179                    target_collection.limits.owner_can_transfer &&1180                    Self::is_owner_or_admin_permissions(collection_id, sender.clone())1181                ),1182                Error::<T>::NoPermission1183            );11841185            if target_collection.access == AccessMode::WhiteList {1186                Self::check_white_list(collection_id, &sender)?;1187                Self::check_white_list(collection_id, &recipient)?;1188            }11891190            // Reduce approval by transferred amount or remove if remaining approval drops to 01191            if approval.checked_sub(value).unwrap_or(0) > 0 {1192                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1193            }1194            else {1195                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1196            }11971198            match target_collection.mode1199            {1200                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1201                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1202                CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1203                _ => ()1204            };12051206            Ok(())1207        }12081209        // #[weight = 0]1210        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {12111212        //     // let no_perm_mes = "You do not have permissions to modify this collection";1213        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1214        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1215        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);12161217        //     // // on_nft_received  call12181219        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;12201221        //     Ok(())1222        // }12231224        /// Set off-chain data schema.1225        /// 1226        /// # Permissions1227        /// 1228        /// * Collection Owner1229        /// * Collection Admin1230        /// 1231        /// # Arguments1232        /// 1233        /// * collection_id.1234        /// 1235        /// * schema: String representing the offchain data schema.1236        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1237        pub fn set_variable_meta_data (1238            origin,1239            collection_id: CollectionId,1240            item_id: TokenId,1241            data: Vec<u8>1242        ) -> DispatchResult {1243            let sender = ensure_signed(origin)?;1244            1245            Self::collection_exists(collection_id)?;1246            Self::token_exists(collection_id, item_id, &sender)?;12471248            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12491250            // Modify permissions check1251            let target_collection = <Collection<T>>::get(collection_id);1252            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1253                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1254                Error::<T>::NoPermission);12551256            match target_collection.mode1257            {1258                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1259                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1260                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1261                _ => fail!(Error::<T>::UnexpectedCollectionType)1262            };12631264            Ok(())1265        }1266 1267        /// Set schema standard1268        /// ImageURL1269        /// Unique1270        /// 1271        /// # Permissions1272        /// 1273        /// * Collection Owner1274        /// * Collection Admin1275        /// 1276        /// # Arguments1277        /// 1278        /// * collection_id.1279        /// 1280        /// * schema: SchemaVersion: enum1281        #[weight = <T as Config>::WeightInfo::set_schema_version()]1282        pub fn set_schema_version(1283            origin,1284            collection_id: CollectionId,1285            version: SchemaVersion1286        ) -> DispatchResult {1287            let sender = ensure_signed(origin)?;1288            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1289            let mut target_collection = <Collection<T>>::get(collection_id);1290            target_collection.schema_version = version;1291            <Collection<T>>::insert(collection_id, target_collection);12921293            Ok(())1294        }12951296        /// Set off-chain data schema.1297        /// 1298        /// # Permissions1299        /// 1300        /// * Collection Owner1301        /// * Collection Admin1302        /// 1303        /// # Arguments1304        /// 1305        /// * collection_id.1306        /// 1307        /// * schema: String representing the offchain data schema.1308        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1309        pub fn set_offchain_schema(1310            origin,1311            collection_id: CollectionId,1312            schema: Vec<u8>1313        ) -> DispatchResult {1314            let sender = ensure_signed(origin)?;1315            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13161317            // check schema limit1318            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");13191320            let mut target_collection = <Collection<T>>::get(collection_id);1321            target_collection.offchain_schema = schema;1322            <Collection<T>>::insert(collection_id, target_collection);13231324            Ok(())1325        }13261327        /// Set const on-chain data schema.1328        /// 1329        /// # Permissions1330        /// 1331        /// * Collection Owner1332        /// * Collection Admin1333        /// 1334        /// # Arguments1335        /// 1336        /// * collection_id.1337        /// 1338        /// * schema: String representing the const on-chain data schema.1339        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1340        pub fn set_const_on_chain_schema (1341            origin,1342            collection_id: CollectionId,1343            schema: Vec<u8>1344        ) -> DispatchResult {1345            let sender = ensure_signed(origin)?;1346            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13471348            // check schema limit1349            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");13501351            let mut target_collection = <Collection<T>>::get(collection_id);1352            target_collection.const_on_chain_schema = schema;1353            <Collection<T>>::insert(collection_id, target_collection);13541355            Ok(())1356        }13571358        /// Set variable on-chain data schema.1359        /// 1360        /// # Permissions1361        /// 1362        /// * Collection Owner1363        /// * Collection Admin1364        /// 1365        /// # Arguments1366        /// 1367        /// * collection_id.1368        /// 1369        /// * schema: String representing the variable on-chain data schema.1370        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1371        pub fn set_variable_on_chain_schema (1372            origin,1373            collection_id: CollectionId,1374            schema: Vec<u8>1375        ) -> DispatchResult {1376            let sender = ensure_signed(origin)?;1377            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13781379            // check schema limit1380            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");13811382            let mut target_collection = <Collection<T>>::get(collection_id);1383            target_collection.variable_on_chain_schema = schema;1384            <Collection<T>>::insert(collection_id, target_collection);13851386            Ok(())1387        }13881389        // Sudo permissions function1390        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1391        pub fn set_chain_limits(1392            origin,1393            limits: ChainLimits1394        ) -> DispatchResult {13951396            #[cfg(not(feature = "runtime-benchmarks"))]1397            ensure_root(origin)?;13981399            <ChainLimit>::put(limits);1400            Ok(())1401        }14021403        /// Enable smart contract self-sponsoring.1404        /// 1405        /// # Permissions1406        /// 1407        /// * Contract Owner1408        /// 1409        /// # Arguments1410        /// 1411        /// * contract address1412        /// * enable flag1413        /// 1414        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1415        pub fn enable_contract_sponsoring(1416            origin,1417            contract_address: T::AccountId,1418            enable: bool1419        ) -> DispatchResult {14201421            let sender = ensure_signed(origin)?;14221423            #[cfg(feature = "runtime-benchmarks")]1424            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14251426            Self::ensure_contract_owned(sender, &contract_address)?;14271428            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1429            Ok(())1430        }14311432        /// Set the rate limit for contract sponsoring to specified number of blocks.1433        /// 1434        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1435        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1436        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1437        /// from contract endowment if there are at least B blocks between such transactions. 1438        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1439        /// 1440        /// # Permissions1441        /// 1442        /// * Contract Owner1443        /// 1444        /// # Arguments1445        /// 1446        /// -`contract_address`: Address of the contract to sponsor1447        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1448        /// 1449        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1450        pub fn set_contract_sponsoring_rate_limit(1451            origin,1452            contract_address: T::AccountId,1453            rate_limit: T::BlockNumber1454        ) -> DispatchResult {1455            let sender = ensure_signed(origin)?;14561457            #[cfg(feature = "runtime-benchmarks")]1458            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14591460            Self::ensure_contract_owned(sender, &contract_address)?;1461            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1462            Ok(())1463        }14641465        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1466        /// 1467        /// # Permissions1468        /// 1469        /// * Address that deployed smart contract.1470        /// 1471        /// # Arguments1472        /// 1473        /// -`contract_address`: Address of the contract.1474        /// 1475        /// - `enable`: .  1476        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1477        pub fn toggle_contract_white_list(1478            origin,1479            contract_address: T::AccountId,1480            enable: bool1481        ) -> DispatchResult {1482            let sender = ensure_signed(origin)?;14831484            #[cfg(feature = "runtime-benchmarks")]1485            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14861487            Self::ensure_contract_owned(sender, &contract_address)?;1488            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1489            Ok(())1490        }1491        1492        /// Add an address to smart contract white list.1493        /// 1494        /// # Permissions1495        /// 1496        /// * Address that deployed smart contract.1497        /// 1498        /// # Arguments1499        /// 1500        /// -`contract_address`: Address of the contract.1501        ///1502        /// -`account_address`: Address to add.1503        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1504        pub fn add_to_contract_white_list(1505            origin,1506            contract_address: T::AccountId,1507            account_address: T::AccountId1508        ) -> DispatchResult {1509            let sender = ensure_signed(origin)?;15101511            #[cfg(feature = "runtime-benchmarks")]1512            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1513            1514            Self::ensure_contract_owned(sender, &contract_address)?;      1515            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1516            Ok(())1517        }15181519        /// Remove an address from smart contract white list.1520        /// 1521        /// # Permissions1522        /// 1523        /// * Address that deployed smart contract.1524        /// 1525        /// # Arguments1526        /// 1527        /// -`contract_address`: Address of the contract.1528        ///1529        /// -`account_address`: Address to remove.1530        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1531        pub fn remove_from_contract_white_list(1532            origin,1533            contract_address: T::AccountId,1534            account_address: T::AccountId1535        ) -> DispatchResult {1536            let sender = ensure_signed(origin)?;15371538            #[cfg(feature = "runtime-benchmarks")]1539            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15401541            Self::ensure_contract_owned(sender, &contract_address)?;1542            <ContractWhiteList<T>>::remove(contract_address, account_address);1543            Ok(())1544        }15451546        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1547        pub fn set_collection_limits(1548            origin,1549            collection_id: u32,1550            new_limits: CollectionLimits,1551        ) -> DispatchResult {1552            let sender = ensure_signed(origin)?;1553            Self::check_owner_permissions(collection_id, sender.clone())?;1554            let mut target_collection = <Collection<T>>::get(collection_id);1555            let old_limits = target_collection.limits;1556            let chain_limits = ChainLimit::get();15571558            // collection bounds1559            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1560                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1561                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1562                Error::<T>::CollectionLimitBoundsExceeded);15631564            // token_limit   check  prev1565            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1566            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);15671568            ensure!(1569                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1570                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1571                Error::<T>::OwnerPermissionsCantBeReverted,1572            );15731574            target_collection.limits = new_limits;1575            <Collection<T>>::insert(collection_id, target_collection);15761577            Ok(())1578        } 1579    }1580}15811582impl<T: Config> Module<T> {15831584    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {15851586        let target_collection = <Collection<T>>::get(collection_id);15871588        // Limits check1589        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;15901591        // Transfer permissions check1592        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1593            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1594            Error::<T>::NoPermission);15951596        if target_collection.access == AccessMode::WhiteList {1597            Self::check_white_list(collection_id, &sender)?;1598            Self::check_white_list(collection_id, &recipient)?;1599        }16001601        match target_collection.mode1602        {1603            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient.clone())?,1604            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1605            CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient.clone())?,1606            _ => ()1607        };16081609        Self::deposit_event(RawEvent::Transfer(collection_id, item_id, sender, recipient, value));16101611        Ok(())1612    }161316141615    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {16161617        // check token limit and account token limit1618        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1619        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1620        1621        Ok(())1622    }16231624    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {16251626        // check token limit and account token limit1627        let total_items: u32 = ItemListIndex::get(collection_id);1628        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1629        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1630        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);16311632        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1633            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1634            Self::check_white_list(collection_id, owner)?;1635            Self::check_white_list(collection_id, sender)?;1636        }16371638        Ok(())1639    }16401641    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1642        match target_collection.mode1643        {1644            CollectionMode::NFT => {1645                if let CreateItemData::NFT(data) = data {1646                    // check sizes1647                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1648                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1649                } else {1650                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1651                }1652            },1653            CollectionMode::Fungible(_) => {1654                if let CreateItemData::Fungible(_) = data {1655                } else {1656                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1657                }1658            },1659            CollectionMode::ReFungible => {1660                if let CreateItemData::ReFungible(data) = data {16611662                    // check sizes1663                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1664                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16651666                    // Check refungibility limits1667                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1668                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1669                } else {1670                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1671                }1672            },1673            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1674        };16751676        Ok(())1677    }16781679    fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1680        match data1681        {1682            CreateItemData::NFT(data) => {1683                let item = NftItemType {1684                    owner: owner.clone(),1685                    const_data: data.const_data,1686                    variable_data: data.variable_data1687                };16881689                Self::add_nft_item(collection_id, item)?;1690            },1691            CreateItemData::Fungible(data) => {1692                Self::add_fungible_item(collection_id, &owner, data.value)?;1693            },1694            CreateItemData::ReFungible(data) => {1695                let mut owner_list = Vec::new();1696                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});16971698                let item = ReFungibleItemType {1699                    owner: owner_list,1700                    const_data: data.const_data,1701                    variable_data: data.variable_data1702                };17031704                Self::add_refungible_item(collection_id, item)?;1705            }1706        };17071708        // call event1709        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id), owner));17101711        Ok(())1712    }17131714    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {17151716        // Does new owner already have an account?1717        let mut balance: u128 = 0;1718        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1719            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1720        } 17211722        // Mint 1723        let item = FungibleItemType {1724            value: balance + value1725        };1726        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);17271728        // Update balance1729        let new_balance = <Balance<T>>::get(collection_id, owner)1730            .checked_add(value)1731            .ok_or(Error::<T>::NumOverflow)?;1732        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17331734        Ok(())1735    }17361737    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1738        let current_index = <ItemListIndex>::get(collection_id)1739            .checked_add(1)1740            .ok_or(Error::<T>::NumOverflow)?;1741        let itemcopy = item.clone();17421743        let value = item.owner.first().unwrap().fraction;1744        let owner = item.owner.first().unwrap().owner.clone();17451746        Self::add_token_index(collection_id, current_index, &owner)?;17471748        <ItemListIndex>::insert(collection_id, current_index);1749        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17501751        // Update balance1752        let new_balance = <Balance<T>>::get(collection_id, &owner)1753            .checked_add(value)1754            .ok_or(Error::<T>::NumOverflow)?;1755        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17561757        Ok(())1758    }17591760    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1761        let current_index = <ItemListIndex>::get(collection_id)1762            .checked_add(1)1763            .ok_or(Error::<T>::NumOverflow)?;17641765        let item_owner = item.owner.clone();1766        Self::add_token_index(collection_id, current_index, &item.owner)?;17671768        <ItemListIndex>::insert(collection_id, current_index);1769        <NftItemList<T>>::insert(collection_id, current_index, item);17701771        // Update balance1772        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1773            .checked_add(1)1774            .ok_or(Error::<T>::NumOverflow)?;1775        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17761777        Ok(())1778    }17791780    fn burn_refungible_item(1781        collection_id: CollectionId,1782        item_id: TokenId,1783        owner: &T::AccountId,1784    ) -> DispatchResult {1785        ensure!(1786            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1787            Error::<T>::TokenNotFound1788        );1789        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1790        let rft_balance = token1791            .owner1792            .iter()1793            .filter(|&i| i.owner == *owner)1794            .next()1795            .unwrap();1796        Self::remove_token_index(collection_id, item_id, owner)?;17971798        // update balance1799        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1800            .checked_sub(rft_balance.fraction)1801            .ok_or(Error::<T>::NumOverflow)?;1802        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);18031804        // Re-create owners list with sender removed1805        let index = token1806            .owner1807            .iter()1808            .position(|i| i.owner == *owner)1809            .unwrap();1810        token.owner.remove(index);1811        let owner_count = token.owner.len();18121813        // Burn the token completely if this was the last (only) owner1814        if owner_count == 0 {1815            <ReFungibleItemList<T>>::remove(collection_id, item_id);1816        }1817        else {1818            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1819        }18201821        Ok(())1822    }18231824    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1825        ensure!(1826            <NftItemList<T>>::contains_key(collection_id, item_id),1827            Error::<T>::TokenNotFound1828        );1829        let item = <NftItemList<T>>::get(collection_id, item_id);1830        Self::remove_token_index(collection_id, item_id, &item.owner)?;18311832        // update balance1833        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1834            .checked_sub(1)1835            .ok_or(Error::<T>::NumOverflow)?;1836        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1837        <NftItemList<T>>::remove(collection_id, item_id);18381839        Ok(())1840    }18411842    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1843        ensure!(1844            <FungibleItemList<T>>::contains_key(collection_id, owner),1845            Error::<T>::TokenNotFound1846        );1847        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1848        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18491850        // update balance1851        let new_balance = <Balance<T>>::get(collection_id, owner)1852            .checked_sub(value)1853            .ok_or(Error::<T>::NumOverflow)?;1854        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18551856        if balance.value - value > 0 {1857            balance.value -= value;1858            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1859        }1860        else {1861            <FungibleItemList<T>>::remove(collection_id, owner);1862        }18631864        Ok(())1865    }18661867    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1868        ensure!(1869            <Collection<T>>::contains_key(collection_id),1870            Error::<T>::CollectionNotFound1871        );1872        Ok(())1873    }18741875    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1876        Self::collection_exists(collection_id)?;18771878        let target_collection = <Collection<T>>::get(collection_id);1879        ensure!(1880            subject == target_collection.owner,1881            Error::<T>::NoPermission1882        );18831884        Ok(())1885    }18861887    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1888        let target_collection = <Collection<T>>::get(collection_id);1889        let mut result: bool = subject == target_collection.owner;1890        let exists = <AdminList<T>>::contains_key(collection_id);18911892        if !result & exists {1893            if <AdminList<T>>::get(collection_id).contains(&subject) {1894                result = true1895            }1896        }18971898        result1899    }19001901    fn check_owner_or_admin_permissions(1902        collection_id: CollectionId,1903        subject: T::AccountId,1904    ) -> DispatchResult {1905        Self::collection_exists(collection_id)?;1906        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());19071908        ensure!(1909            result,1910            Error::<T>::NoPermission1911        );1912        Ok(())1913    }19141915    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1916        let target_collection = <Collection<T>>::get(collection_id);19171918        match target_collection.mode {1919            CollectionMode::NFT => {1920                <NftItemList<T>>::get(collection_id, item_id).owner == subject1921            }1922            CollectionMode::Fungible(_) => {1923                <FungibleItemList<T>>::contains_key(collection_id, &subject)1924            }1925            CollectionMode::ReFungible => {1926                <ReFungibleItemList<T>>::get(collection_id, item_id)1927                    .owner1928                    .iter()1929                    .any(|i| i.owner == subject)1930            }1931            CollectionMode::Invalid => false,1932        }1933    }19341935    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1936        let mes = Error::<T>::AddresNotInWhiteList;1937        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);19381939        Ok(())1940    }19411942    /// Check if token exists. In case of Fungible, check if there is an entry for 1943    /// the owner in fungible balances double map1944    fn token_exists(1945        collection_id: CollectionId,1946        item_id: TokenId,1947        owner: &T::AccountId1948    ) -> DispatchResult {1949        let target_collection = <Collection<T>>::get(collection_id);1950        let exists = match target_collection.mode1951        {1952            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1953            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),1954            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1955            _ => false1956        };19571958        ensure!(exists == true, Error::<T>::TokenNotFound);1959        Ok(())1960    }19611962    fn transfer_fungible(1963        collection_id: CollectionId,1964        value: u128,1965        owner: &T::AccountId,1966        recipient: &T::AccountId,1967    ) -> DispatchResult {1968        Self::token_exists(collection_id, 0, owner)?;19691970        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1971        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19721973        // Send balance to recipient (updates balanceOf of recipient)1974        Self::add_fungible_item(collection_id, recipient, value)?;19751976        // update balanceOf of sender1977        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);19781979        // Reduce or remove sender1980        if balance.value == value {1981            <FungibleItemList<T>>::remove(collection_id, owner);1982        }1983        else {1984            balance.value -= value;1985            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1986        }19871988        Ok(())1989    }19901991    fn transfer_refungible(1992        collection_id: CollectionId,1993        item_id: TokenId,1994        value: u128,1995        owner: T::AccountId,1996        new_owner: T::AccountId,1997    ) -> DispatchResult {1998        Self::token_exists(collection_id, item_id, &owner)?;19992000        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);2001        let item = full_item2002            .owner2003            .iter()2004            .filter(|i| i.owner == owner)2005            .next()2006            .ok_or(Error::<T>::NumOverflow)?;2007        let amount = item.fraction;20082009        ensure!(amount >= value, Error::<T>::TokenValueTooLow);20102011        // update balance2012        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2013            .checked_sub(value)2014            .ok_or(Error::<T>::NumOverflow)?;2015        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20162017        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2018            .checked_add(value)2019            .ok_or(Error::<T>::NumOverflow)?;2020        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20212022        let old_owner = item.owner.clone();2023        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20242025        // transfer2026        if amount == value && !new_owner_has_account {2027            // change owner2028            // new owner do not have account2029            let mut new_full_item = full_item.clone();2030            new_full_item2031                .owner2032                .iter_mut()2033                .find(|i| i.owner == owner)2034                .unwrap()2035                .owner = new_owner.clone();2036            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20372038            // update index collection2039            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2040        } else {2041            let mut new_full_item = full_item.clone();2042            new_full_item2043                .owner2044                .iter_mut()2045                .find(|i| i.owner == owner)2046                .unwrap()2047                .fraction -= value;20482049            // separate amount2050            if new_owner_has_account {2051                // new owner has account2052                new_full_item2053                    .owner2054                    .iter_mut()2055                    .find(|i| i.owner == new_owner)2056                    .unwrap()2057                    .fraction += value;2058            } else {2059                // new owner do not have account2060                new_full_item.owner.push(Ownership {2061                    owner: new_owner.clone(),2062                    fraction: value,2063                });2064                Self::add_token_index(collection_id, item_id, &new_owner)?;2065            }20662067            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2068        }20692070        Ok(())2071    }20722073    fn transfer_nft(2074        collection_id: CollectionId,2075        item_id: TokenId,2076        sender: T::AccountId,2077        new_owner: T::AccountId,2078    ) -> DispatchResult {2079        Self::token_exists(collection_id, item_id, &sender)?;20802081        let mut item = <NftItemList<T>>::get(collection_id, item_id);20822083        ensure!(2084            sender == item.owner,2085            Error::<T>::MustBeTokenOwner2086        );20872088        // update balance2089        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2090            .checked_sub(1)2091            .ok_or(Error::<T>::NumOverflow)?;2092        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20932094        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2095            .checked_add(1)2096            .ok_or(Error::<T>::NumOverflow)?;2097        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20982099        // change owner2100        let old_owner = item.owner.clone();2101        item.owner = new_owner.clone();2102        <NftItemList<T>>::insert(collection_id, item_id, item);21032104        // update index collection2105        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21062107        Ok(())2108    }2109    2110    fn set_re_fungible_variable_data(2111        collection_id: CollectionId,2112        item_id: TokenId,2113        data: Vec<u8>2114    ) -> DispatchResult {2115        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);21162117        item.variable_data = data;21182119        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21202121        Ok(())2122    }21232124    fn set_nft_variable_data(2125        collection_id: CollectionId,2126        item_id: TokenId,2127        data: Vec<u8>2128    ) -> DispatchResult {2129        let mut item = <NftItemList<T>>::get(collection_id, item_id);2130        2131        item.variable_data = data;21322133        <NftItemList<T>>::insert(collection_id, item_id, item);2134        2135        Ok(())2136    }21372138    fn init_collection(item: &CollectionType<T::AccountId>) {2139        // check params2140        assert!(2141            item.decimal_points <= MAX_DECIMAL_POINTS,2142            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2143        );2144        assert!(2145            item.name.len() <= 64,2146            "Collection name can not be longer than 63 char"2147        );2148        assert!(2149            item.name.len() <= 256,2150            "Collection description can not be longer than 255 char"2151        );2152        assert!(2153            item.token_prefix.len() <= 16,2154            "Token prefix can not be longer than 15 char"2155        );21562157        // Generate next collection ID2158        let next_id = CreatedCollectionCount::get()2159            .checked_add(1)2160            .unwrap();21612162        CreatedCollectionCount::put(next_id);2163    }21642165    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2166        let current_index = <ItemListIndex>::get(collection_id)2167            .checked_add(1)2168            .unwrap();21692170        let item_owner = item.owner.clone();2171        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21722173        <ItemListIndex>::insert(collection_id, current_index);21742175        // Update balance2176        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2177            .checked_add(1)2178            .unwrap();2179        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2180    }21812182    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2183        let current_index = <ItemListIndex>::get(collection_id)2184            .checked_add(1)2185            .unwrap();21862187        Self::add_token_index(collection_id, current_index, owner).unwrap();21882189        <ItemListIndex>::insert(collection_id, current_index);21902191        // Update balance2192        let new_balance = <Balance<T>>::get(collection_id, owner)2193            .checked_add(item.value)2194            .unwrap();2195        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2196    }21972198    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2199        let current_index = <ItemListIndex>::get(collection_id)2200            .checked_add(1)2201            .unwrap();22022203        let value = item.owner.first().unwrap().fraction;2204        let owner = item.owner.first().unwrap().owner.clone();22052206        Self::add_token_index(collection_id, current_index, &owner).unwrap();22072208        <ItemListIndex>::insert(collection_id, current_index);22092210        // Update balance2211        let new_balance = <Balance<T>>::get(collection_id, &owner)2212            .checked_add(value)2213            .unwrap();2214        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2215    }22162217    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {22182219        // add to account limit2220        if <AccountItemCount<T>>::contains_key(owner) {22212222            // bound Owned tokens by a single address2223            let count = <AccountItemCount<T>>::get(owner);2224            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);22252226            <AccountItemCount<T>>::insert(owner.clone(), count2227                .checked_add(1)2228                .ok_or(Error::<T>::NumOverflow)?);2229        }2230        else {2231            <AccountItemCount<T>>::insert(owner.clone(), 1);2232        }22332234        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2235        if list_exists {2236            let mut list = <AddressTokens<T>>::get(collection_id, owner);2237            let item_contains = list.contains(&item_index.clone());22382239            if !item_contains {2240                list.push(item_index.clone());2241            }22422243            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2244        } else {2245            let mut itm = Vec::new();2246            itm.push(item_index.clone());2247            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2248        }22492250        Ok(())2251    }22522253    fn remove_token_index(2254        collection_id: CollectionId,2255        item_index: TokenId,2256        owner: &T::AccountId,2257    ) -> DispatchResult {22582259        // update counter2260        <AccountItemCount<T>>::insert(owner.clone(), 2261            <AccountItemCount<T>>::get(owner)2262            .checked_sub(1)2263            .ok_or(Error::<T>::NumOverflow)?);226422652266        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2267        if list_exists {2268            let mut list = <AddressTokens<T>>::get(collection_id, owner);2269            let item_contains = list.contains(&item_index.clone());22702271            if item_contains {2272                list.retain(|&item| item != item_index);2273                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2274            }2275        }22762277        Ok(())2278    }22792280    fn move_token_index(2281        collection_id: CollectionId,2282        item_index: TokenId,2283        old_owner: &T::AccountId,2284        new_owner: &T::AccountId,2285    ) -> DispatchResult {2286        Self::remove_token_index(collection_id, item_index, old_owner)?;2287        Self::add_token_index(collection_id, item_index, new_owner)?;22882289        Ok(())2290    }2291    2292    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2293        if <ContractOwner<T>>::contains_key(contract.clone()) {2294            let owner = <ContractOwner<T>>::get(contract);2295            ensure!(account == owner, Error::<T>::NoPermission);2296        } else {2297            fail!(Error::<T>::NoPermission);2298        }22992300        Ok(())2301    }2302}23032304////////////////////////////////////////////////////////////////////////////////////////////////////2305// Economic models2306// #region23072308/// Fee multiplier.2309pub type Multiplier = FixedU128;23102311type BalanceOf<T> = <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::Balance;23122313/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2314/// in the queue.2315#[derive(Encode, Decode, Clone, Eq, PartialEq)]2316pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);23172318impl<T: Config + Send + Sync> sp_std::fmt::Debug 2319    for ChargeTransactionPayment<T>2320{2321	#[cfg(feature = "std")]2322	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2323		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2324	}2325	#[cfg(not(feature = "std"))]2326	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2327		Ok(())2328	}2329}23302331impl<T: Config> ChargeTransactionPayment<T>2332where2333    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2334    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2335    T::AccountId: AsRef<[u8]>,2336    T::AccountId: UncheckedFrom<T::Hash>,2337{2338    fn traditional_fee(2339        len: usize,2340        info: &DispatchInfoOf<T::Call>,2341        tip: BalanceOf<T>,2342    ) -> BalanceOf<T>2343    where2344        T::Call: Dispatchable<Info = DispatchInfo>,2345    {2346        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2347    }23482349	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2350        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2351        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2352        let len_saturation = max_block_length as u64 / (len as u64).max(1);2353        let coefficient: BalanceOf<T> = weight_saturation2354            .min(len_saturation)2355            .saturated_into::<BalanceOf<T>>();2356        final_fee2357            .saturating_mul(coefficient)2358            .saturated_into::<TransactionPriority>()2359    }23602361    fn withdraw_fee(2362        &self,2363        who: &T::AccountId,2364        call: &T::Call,2365        info: &DispatchInfoOf<T::Call>,2366        len: usize,2367	) -> Result<2368		(2369			BalanceOf<T>,2370			<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2371		),2372		TransactionValidityError,2373	> {2374        let tip = self.0;23752376        // Set fee based on call type. Creating collection costs 1 Unique.2377        // All other transactions have traditional fees so far2378        // let fee = match call.is_sub_type() {2379        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2380        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2381        //                                                 // _ => <BalanceOf<T>>::from(100)2382        // };2383        let fee = Self::traditional_fee(len, info, tip);23842385        // Only mess with balances if fee is not zero.2386        if fee.is_zero() {2387            return <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2388			.map(|i| (fee, i));2389        }23902391        // Determine who is paying transaction fee based on ecnomic model2392        // Parse call to extract collection ID and access collection sponsor2393        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2394            Some(Call::create_item(collection_id, _owner, _properties)) => {23952396                // sponsor timeout2397                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;23982399                let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;2400                let mut sponsored = true;2401                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2402                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2403                    let limit_time = last_tx_block + limit.into();2404                    if block_number <= limit_time {2405                        sponsored = false;2406                    }2407                }2408                if sponsored {2409                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2410                }24112412                // check free create limit2413                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2414                   (<Collection<T>>::get(collection_id).sponsor_confirmed) &&2415                   (sponsored)2416                {2417                    <Collection<T>>::get(collection_id).sponsor2418                } else {2419                    T::AccountId::default()2420                }2421            }2422            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2423                2424                let mut sponsor_transfer = false;2425                if <Collection<T>>::get(collection_id).sponsor_confirmed {24262427                    let collection_limits = <Collection<T>>::get(collection_id).limits;2428                    let collection_mode = <Collection<T>>::get(collection_id).mode;2429    2430                    // sponsor timeout2431                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2432                    sponsor_transfer = match collection_mode {2433                        CollectionMode::NFT => {2434    2435                            // get correct limit2436                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2437                                collection_limits.sponsor_transfer_timeout2438                            } else {2439                                ChainLimit::get().nft_sponsor_transfer_timeout2440                            };2441    2442                            let mut sponsored = true;2443                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2444                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2445                                let limit_time = last_tx_block + limit.into();2446                                if block_number <= limit_time {2447                                    sponsored = false;2448                                }2449                            }2450                            if sponsored {2451                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2452                            }24532454                            sponsored2455                        }2456                        CollectionMode::Fungible(_) => {2457    2458                            // get correct limit2459                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2460                                collection_limits.sponsor_transfer_timeout2461                            } else {2462                                ChainLimit::get().fungible_sponsor_transfer_timeout2463                            };2464    2465                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2466                            let mut sponsored = true;2467                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2468                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2469                                let limit_time = last_tx_block + limit.into();2470                                if block_number <= limit_time {2471                                    sponsored = false;2472                                }2473                            }2474                            if sponsored {2475                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2476                            }24772478                            sponsored2479                        }2480                        CollectionMode::ReFungible => {2481    2482                            // get correct limit2483                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2484                                collection_limits.sponsor_transfer_timeout2485                            } else {2486                                ChainLimit::get().refungible_sponsor_transfer_timeout2487                            };2488    2489                            let mut sponsored = true;2490                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2491                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2492                                let limit_time = last_tx_block + limit.into();2493                                if block_number <= limit_time {2494                                    sponsored = false;2495                                }2496                            }2497                            if sponsored {2498                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2499                            }25002501                            sponsored2502                        }2503                        _ => {2504                            false2505                        },2506                    };2507                }25082509                if !sponsor_transfer {2510                    T::AccountId::default()2511                } else {2512                    <Collection<T>>::get(collection_id).sponsor2513                }2514            }25152516            _ => T::AccountId::default(),2517        };25182519        // Sponsor smart contracts2520        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {25212522            // On instantiation: set the contract owner2523            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {25242525                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2526                    &who,2527                    code_hash,2528                    salt,2529                );2530                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());25312532                T::AccountId::default()2533            },25342535            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2536            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {25372538                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());25392540                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2541                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2542                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2543                  2544                if !owned_contract && white_list_enabled {2545                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2546                        return Err(InvalidTransaction::Call.into());2547                    }2548                }25492550                let mut sponsor_transfer = false;2551                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2552                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2553                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2554                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2555                    let limit_time = last_tx_block + rate_limit;25562557                    if block_number >= limit_time {2558                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2559                        sponsor_transfer = true;2560                    }2561                } else {2562                    sponsor_transfer = false;2563                }2564               2565                2566                let mut sp = T::AccountId::default();2567                if sponsor_transfer {2568                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2569                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2570                            sp = called_contract;2571                        }2572                    }2573                }25742575                sp2576            },25772578            _ => sponsor,2579        };25802581        let mut who_pays_fee: T::AccountId = sponsor.clone();2582        if sponsor == T::AccountId::default() {2583            who_pays_fee = who.clone();2584        }25852586		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2587			.map(|i| (fee, i))2588    }2589}259025912592impl<T: Config + Send + Sync> SignedExtension2593    for ChargeTransactionPayment<T>2594where2595    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2596    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2597    T::AccountId: AsRef<[u8]>,2598    T::AccountId: UncheckedFrom<T::Hash>,2599{2600    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2601    type AccountId = T::AccountId;2602    type Call = T::Call;2603    type AdditionalSigned = ();2604    type Pre = (2605        // tip2606        BalanceOf<T>,2607        // who pays fee2608        Self::AccountId,2609		// imbalance resulting from withdrawing the fee2610		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2611    );2612    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2613        Ok(())2614    }26152616    fn validate(2617        &self,2618        who: &Self::AccountId,2619        call: &Self::Call,2620        info: &DispatchInfoOf<Self::Call>,2621        len: usize,2622    ) -> TransactionValidity {2623		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2624		Ok(ValidTransaction {2625			priority: Self::get_priority(len, info, fee),2626			..Default::default()2627		})2628    }26292630    fn pre_dispatch(2631        self,2632        who: &Self::AccountId,2633        call: &Self::Call,2634        info: &DispatchInfoOf<Self::Call>,2635        len: usize,2636    ) -> Result<Self::Pre, TransactionValidityError> {2637        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2638        Ok((self.0, who.clone(), imbalance))2639    }26402641    fn post_dispatch(2642        pre: Self::Pre,2643        info: &DispatchInfoOf<Self::Call>,2644        post_info: &PostDispatchInfoOf<Self::Call>,2645        len: usize,2646        _result: &DispatchResult,2647    ) -> Result<(), TransactionValidityError> {2648		let (tip, who, imbalance) = pre;2649		let actual_fee = transaction_payment::Module::<T>::compute_actual_fee(2650			len as u32,2651			info,2652			post_info,2653			tip,2654		);2655		<T as transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2656		Ok(())2657    }2658}26592660// #endregion