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

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                new_limits.sponsored_mint_size <= chain_limits.custom_data_limit,1563                Error::<T>::CollectionLimitBoundsExceeded);15641565            // token_limit   check  prev1566            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1567            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);15681569            ensure!(1570                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1571                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1572                Error::<T>::OwnerPermissionsCantBeReverted,1573            );15741575            target_collection.limits = new_limits;1576            <Collection<T>>::insert(collection_id, target_collection);15771578            Ok(())1579        } 1580    }1581}15821583impl<T: Config> Module<T> {15841585    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {15861587        let target_collection = <Collection<T>>::get(collection_id);15881589        // Limits check1590        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;15911592        // Transfer permissions check1593        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1594            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1595            Error::<T>::NoPermission);15961597        if target_collection.access == AccessMode::WhiteList {1598            Self::check_white_list(collection_id, &sender)?;1599            Self::check_white_list(collection_id, &recipient)?;1600        }16011602        match target_collection.mode1603        {1604            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient.clone())?,1605            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1606            CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient.clone())?,1607            _ => ()1608        };16091610        Self::deposit_event(RawEvent::Transfer(collection_id, item_id, sender, recipient, value));16111612        Ok(())1613    }161416151616    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {16171618        // check token limit and account token limit1619        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1620        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1621        1622        Ok(())1623    }16241625    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {16261627        // check token limit and account token limit1628        let total_items: u32 = ItemListIndex::get(collection_id);1629        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1630        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1631        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);16321633        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1634            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1635            Self::check_white_list(collection_id, owner)?;1636            Self::check_white_list(collection_id, sender)?;1637        }16381639        Ok(())1640    }16411642    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1643        match target_collection.mode1644        {1645            CollectionMode::NFT => {1646                if let CreateItemData::NFT(data) = data {1647                    // check sizes1648                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1649                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1650                } else {1651                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1652                }1653            },1654            CollectionMode::Fungible(_) => {1655                if let CreateItemData::Fungible(_) = data {1656                } else {1657                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1658                }1659            },1660            CollectionMode::ReFungible => {1661                if let CreateItemData::ReFungible(data) = data {16621663                    // check sizes1664                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1665                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16661667                    // Check refungibility limits1668                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1669                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1670                } else {1671                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1672                }1673            },1674            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1675        };16761677        Ok(())1678    }16791680    fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1681        match data1682        {1683            CreateItemData::NFT(data) => {1684                let item = NftItemType {1685                    owner: owner.clone(),1686                    const_data: data.const_data,1687                    variable_data: data.variable_data1688                };16891690                Self::add_nft_item(collection_id, item)?;1691            },1692            CreateItemData::Fungible(data) => {1693                Self::add_fungible_item(collection_id, &owner, data.value)?;1694            },1695            CreateItemData::ReFungible(data) => {1696                let mut owner_list = Vec::new();1697                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});16981699                let item = ReFungibleItemType {1700                    owner: owner_list,1701                    const_data: data.const_data,1702                    variable_data: data.variable_data1703                };17041705                Self::add_refungible_item(collection_id, item)?;1706            }1707        };17081709        // call event1710        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id), owner));17111712        Ok(())1713    }17141715    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {17161717        // Does new owner already have an account?1718        let mut balance: u128 = 0;1719        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1720            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1721        } 17221723        // Mint 1724        let item = FungibleItemType {1725            value: balance + value1726        };1727        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);17281729        // Update balance1730        let new_balance = <Balance<T>>::get(collection_id, owner)1731            .checked_add(value)1732            .ok_or(Error::<T>::NumOverflow)?;1733        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17341735        Ok(())1736    }17371738    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1739        let current_index = <ItemListIndex>::get(collection_id)1740            .checked_add(1)1741            .ok_or(Error::<T>::NumOverflow)?;1742        let itemcopy = item.clone();17431744        let value = item.owner.first().unwrap().fraction;1745        let owner = item.owner.first().unwrap().owner.clone();17461747        Self::add_token_index(collection_id, current_index, &owner)?;17481749        <ItemListIndex>::insert(collection_id, current_index);1750        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17511752        // Update balance1753        let new_balance = <Balance<T>>::get(collection_id, &owner)1754            .checked_add(value)1755            .ok_or(Error::<T>::NumOverflow)?;1756        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17571758        Ok(())1759    }17601761    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1762        let current_index = <ItemListIndex>::get(collection_id)1763            .checked_add(1)1764            .ok_or(Error::<T>::NumOverflow)?;17651766        let item_owner = item.owner.clone();1767        Self::add_token_index(collection_id, current_index, &item.owner)?;17681769        <ItemListIndex>::insert(collection_id, current_index);1770        <NftItemList<T>>::insert(collection_id, current_index, item);17711772        // Update balance1773        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1774            .checked_add(1)1775            .ok_or(Error::<T>::NumOverflow)?;1776        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17771778        Ok(())1779    }17801781    fn burn_refungible_item(1782        collection_id: CollectionId,1783        item_id: TokenId,1784        owner: &T::AccountId,1785    ) -> DispatchResult {1786        ensure!(1787            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1788            Error::<T>::TokenNotFound1789        );1790        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1791        let rft_balance = token1792            .owner1793            .iter()1794            .filter(|&i| i.owner == *owner)1795            .next()1796            .unwrap();1797        Self::remove_token_index(collection_id, item_id, owner)?;17981799        // update balance1800        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1801            .checked_sub(rft_balance.fraction)1802            .ok_or(Error::<T>::NumOverflow)?;1803        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);18041805        // Re-create owners list with sender removed1806        let index = token1807            .owner1808            .iter()1809            .position(|i| i.owner == *owner)1810            .unwrap();1811        token.owner.remove(index);1812        let owner_count = token.owner.len();18131814        // Burn the token completely if this was the last (only) owner1815        if owner_count == 0 {1816            <ReFungibleItemList<T>>::remove(collection_id, item_id);1817        }1818        else {1819            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1820        }18211822        Ok(())1823    }18241825    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1826        ensure!(1827            <NftItemList<T>>::contains_key(collection_id, item_id),1828            Error::<T>::TokenNotFound1829        );1830        let item = <NftItemList<T>>::get(collection_id, item_id);1831        Self::remove_token_index(collection_id, item_id, &item.owner)?;18321833        // update balance1834        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1835            .checked_sub(1)1836            .ok_or(Error::<T>::NumOverflow)?;1837        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1838        <NftItemList<T>>::remove(collection_id, item_id);18391840        Ok(())1841    }18421843    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1844        ensure!(1845            <FungibleItemList<T>>::contains_key(collection_id, owner),1846            Error::<T>::TokenNotFound1847        );1848        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1849        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18501851        // update balance1852        let new_balance = <Balance<T>>::get(collection_id, owner)1853            .checked_sub(value)1854            .ok_or(Error::<T>::NumOverflow)?;1855        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18561857        if balance.value - value > 0 {1858            balance.value -= value;1859            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1860        }1861        else {1862            <FungibleItemList<T>>::remove(collection_id, owner);1863        }18641865        Ok(())1866    }18671868    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1869        ensure!(1870            <Collection<T>>::contains_key(collection_id),1871            Error::<T>::CollectionNotFound1872        );1873        Ok(())1874    }18751876    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1877        Self::collection_exists(collection_id)?;18781879        let target_collection = <Collection<T>>::get(collection_id);1880        ensure!(1881            subject == target_collection.owner,1882            Error::<T>::NoPermission1883        );18841885        Ok(())1886    }18871888    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1889        let target_collection = <Collection<T>>::get(collection_id);1890        let mut result: bool = subject == target_collection.owner;1891        let exists = <AdminList<T>>::contains_key(collection_id);18921893        if !result & exists {1894            if <AdminList<T>>::get(collection_id).contains(&subject) {1895                result = true1896            }1897        }18981899        result1900    }19011902    fn check_owner_or_admin_permissions(1903        collection_id: CollectionId,1904        subject: T::AccountId,1905    ) -> DispatchResult {1906        Self::collection_exists(collection_id)?;1907        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());19081909        ensure!(1910            result,1911            Error::<T>::NoPermission1912        );1913        Ok(())1914    }19151916    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1917        let target_collection = <Collection<T>>::get(collection_id);19181919        match target_collection.mode {1920            CollectionMode::NFT => {1921                <NftItemList<T>>::get(collection_id, item_id).owner == subject1922            }1923            CollectionMode::Fungible(_) => {1924                <FungibleItemList<T>>::contains_key(collection_id, &subject)1925            }1926            CollectionMode::ReFungible => {1927                <ReFungibleItemList<T>>::get(collection_id, item_id)1928                    .owner1929                    .iter()1930                    .any(|i| i.owner == subject)1931            }1932            CollectionMode::Invalid => false,1933        }1934    }19351936    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1937        let mes = Error::<T>::AddresNotInWhiteList;1938        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);19391940        Ok(())1941    }19421943    /// Check if token exists. In case of Fungible, check if there is an entry for 1944    /// the owner in fungible balances double map1945    fn token_exists(1946        collection_id: CollectionId,1947        item_id: TokenId,1948        owner: &T::AccountId1949    ) -> DispatchResult {1950        let target_collection = <Collection<T>>::get(collection_id);1951        let exists = match target_collection.mode1952        {1953            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1954            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),1955            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1956            _ => false1957        };19581959        ensure!(exists == true, Error::<T>::TokenNotFound);1960        Ok(())1961    }19621963    fn transfer_fungible(1964        collection_id: CollectionId,1965        value: u128,1966        owner: &T::AccountId,1967        recipient: &T::AccountId,1968    ) -> DispatchResult {1969        Self::token_exists(collection_id, 0, owner)?;19701971        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1972        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19731974        // Send balance to recipient (updates balanceOf of recipient)1975        Self::add_fungible_item(collection_id, recipient, value)?;19761977        // update balanceOf of sender1978        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);19791980        // Reduce or remove sender1981        if balance.value == value {1982            <FungibleItemList<T>>::remove(collection_id, owner);1983        }1984        else {1985            balance.value -= value;1986            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1987        }19881989        Ok(())1990    }19911992    fn transfer_refungible(1993        collection_id: CollectionId,1994        item_id: TokenId,1995        value: u128,1996        owner: T::AccountId,1997        new_owner: T::AccountId,1998    ) -> DispatchResult {1999        Self::token_exists(collection_id, item_id, &owner)?;20002001        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);2002        let item = full_item2003            .owner2004            .iter()2005            .filter(|i| i.owner == owner)2006            .next()2007            .ok_or(Error::<T>::NumOverflow)?;2008        let amount = item.fraction;20092010        ensure!(amount >= value, Error::<T>::TokenValueTooLow);20112012        // update balance2013        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2014            .checked_sub(value)2015            .ok_or(Error::<T>::NumOverflow)?;2016        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20172018        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2019            .checked_add(value)2020            .ok_or(Error::<T>::NumOverflow)?;2021        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20222023        let old_owner = item.owner.clone();2024        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20252026        // transfer2027        if amount == value && !new_owner_has_account {2028            // change owner2029            // new owner do not have account2030            let mut new_full_item = full_item.clone();2031            new_full_item2032                .owner2033                .iter_mut()2034                .find(|i| i.owner == owner)2035                .unwrap()2036                .owner = new_owner.clone();2037            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20382039            // update index collection2040            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2041        } else {2042            let mut new_full_item = full_item.clone();2043            new_full_item2044                .owner2045                .iter_mut()2046                .find(|i| i.owner == owner)2047                .unwrap()2048                .fraction -= value;20492050            // separate amount2051            if new_owner_has_account {2052                // new owner has account2053                new_full_item2054                    .owner2055                    .iter_mut()2056                    .find(|i| i.owner == new_owner)2057                    .unwrap()2058                    .fraction += value;2059            } else {2060                // new owner do not have account2061                new_full_item.owner.push(Ownership {2062                    owner: new_owner.clone(),2063                    fraction: value,2064                });2065                Self::add_token_index(collection_id, item_id, &new_owner)?;2066            }20672068            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2069        }20702071        Ok(())2072    }20732074    fn transfer_nft(2075        collection_id: CollectionId,2076        item_id: TokenId,2077        sender: T::AccountId,2078        new_owner: T::AccountId,2079    ) -> DispatchResult {2080        Self::token_exists(collection_id, item_id, &sender)?;20812082        let mut item = <NftItemList<T>>::get(collection_id, item_id);20832084        ensure!(2085            sender == item.owner,2086            Error::<T>::MustBeTokenOwner2087        );20882089        // update balance2090        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2091            .checked_sub(1)2092            .ok_or(Error::<T>::NumOverflow)?;2093        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20942095        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2096            .checked_add(1)2097            .ok_or(Error::<T>::NumOverflow)?;2098        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20992100        // change owner2101        let old_owner = item.owner.clone();2102        item.owner = new_owner.clone();2103        <NftItemList<T>>::insert(collection_id, item_id, item);21042105        // update index collection2106        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21072108        Ok(())2109    }2110    2111    fn set_re_fungible_variable_data(2112        collection_id: CollectionId,2113        item_id: TokenId,2114        data: Vec<u8>2115    ) -> DispatchResult {2116        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);21172118        item.variable_data = data;21192120        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21212122        Ok(())2123    }21242125    fn set_nft_variable_data(2126        collection_id: CollectionId,2127        item_id: TokenId,2128        data: Vec<u8>2129    ) -> DispatchResult {2130        let mut item = <NftItemList<T>>::get(collection_id, item_id);2131        2132        item.variable_data = data;21332134        <NftItemList<T>>::insert(collection_id, item_id, item);2135        2136        Ok(())2137    }21382139    fn init_collection(item: &CollectionType<T::AccountId>) {2140        // check params2141        assert!(2142            item.decimal_points <= MAX_DECIMAL_POINTS,2143            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2144        );2145        assert!(2146            item.name.len() <= 64,2147            "Collection name can not be longer than 63 char"2148        );2149        assert!(2150            item.name.len() <= 256,2151            "Collection description can not be longer than 255 char"2152        );2153        assert!(2154            item.token_prefix.len() <= 16,2155            "Token prefix can not be longer than 15 char"2156        );21572158        // Generate next collection ID2159        let next_id = CreatedCollectionCount::get()2160            .checked_add(1)2161            .unwrap();21622163        CreatedCollectionCount::put(next_id);2164    }21652166    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2167        let current_index = <ItemListIndex>::get(collection_id)2168            .checked_add(1)2169            .unwrap();21702171        let item_owner = item.owner.clone();2172        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21732174        <ItemListIndex>::insert(collection_id, current_index);21752176        // Update balance2177        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2178            .checked_add(1)2179            .unwrap();2180        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2181    }21822183    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2184        let current_index = <ItemListIndex>::get(collection_id)2185            .checked_add(1)2186            .unwrap();21872188        Self::add_token_index(collection_id, current_index, owner).unwrap();21892190        <ItemListIndex>::insert(collection_id, current_index);21912192        // Update balance2193        let new_balance = <Balance<T>>::get(collection_id, owner)2194            .checked_add(item.value)2195            .unwrap();2196        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2197    }21982199    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2200        let current_index = <ItemListIndex>::get(collection_id)2201            .checked_add(1)2202            .unwrap();22032204        let value = item.owner.first().unwrap().fraction;2205        let owner = item.owner.first().unwrap().owner.clone();22062207        Self::add_token_index(collection_id, current_index, &owner).unwrap();22082209        <ItemListIndex>::insert(collection_id, current_index);22102211        // Update balance2212        let new_balance = <Balance<T>>::get(collection_id, &owner)2213            .checked_add(value)2214            .unwrap();2215        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2216    }22172218    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {22192220        // add to account limit2221        if <AccountItemCount<T>>::contains_key(owner) {22222223            // bound Owned tokens by a single address2224            let count = <AccountItemCount<T>>::get(owner);2225            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);22262227            <AccountItemCount<T>>::insert(owner.clone(), count2228                .checked_add(1)2229                .ok_or(Error::<T>::NumOverflow)?);2230        }2231        else {2232            <AccountItemCount<T>>::insert(owner.clone(), 1);2233        }22342235        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2236        if list_exists {2237            let mut list = <AddressTokens<T>>::get(collection_id, owner);2238            let item_contains = list.contains(&item_index.clone());22392240            if !item_contains {2241                list.push(item_index.clone());2242            }22432244            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2245        } else {2246            let mut itm = Vec::new();2247            itm.push(item_index.clone());2248            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2249        }22502251        Ok(())2252    }22532254    fn remove_token_index(2255        collection_id: CollectionId,2256        item_index: TokenId,2257        owner: &T::AccountId,2258    ) -> DispatchResult {22592260        // update counter2261        <AccountItemCount<T>>::insert(owner.clone(), 2262            <AccountItemCount<T>>::get(owner)2263            .checked_sub(1)2264            .ok_or(Error::<T>::NumOverflow)?);226522662267        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2268        if list_exists {2269            let mut list = <AddressTokens<T>>::get(collection_id, owner);2270            let item_contains = list.contains(&item_index.clone());22712272            if item_contains {2273                list.retain(|&item| item != item_index);2274                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2275            }2276        }22772278        Ok(())2279    }22802281    fn move_token_index(2282        collection_id: CollectionId,2283        item_index: TokenId,2284        old_owner: &T::AccountId,2285        new_owner: &T::AccountId,2286    ) -> DispatchResult {2287        Self::remove_token_index(collection_id, item_index, old_owner)?;2288        Self::add_token_index(collection_id, item_index, new_owner)?;22892290        Ok(())2291    }2292    2293    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2294        if <ContractOwner<T>>::contains_key(contract.clone()) {2295            let owner = <ContractOwner<T>>::get(contract);2296            ensure!(account == owner, Error::<T>::NoPermission);2297        } else {2298            fail!(Error::<T>::NoPermission);2299        }23002301        Ok(())2302    }2303}23042305////////////////////////////////////////////////////////////////////////////////////////////////////2306// Economic models2307// #region23082309/// Fee multiplier.2310pub type Multiplier = FixedU128;23112312type BalanceOf<T> = <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::Balance;23132314/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2315/// in the queue.2316#[derive(Encode, Decode, Clone, Eq, PartialEq)]2317pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);23182319impl<T: Config + Send + Sync> sp_std::fmt::Debug 2320    for ChargeTransactionPayment<T>2321{2322	#[cfg(feature = "std")]2323	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2324		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2325	}2326	#[cfg(not(feature = "std"))]2327	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2328		Ok(())2329	}2330}23312332impl<T: Config> ChargeTransactionPayment<T>2333where2334    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2335    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2336    T::AccountId: AsRef<[u8]>,2337    T::AccountId: UncheckedFrom<T::Hash>,2338{2339    fn traditional_fee(2340        len: usize,2341        info: &DispatchInfoOf<T::Call>,2342        tip: BalanceOf<T>,2343    ) -> BalanceOf<T>2344    where2345        T::Call: Dispatchable<Info = DispatchInfo>,2346    {2347        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2348    }23492350	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2351        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2352        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2353        let len_saturation = max_block_length as u64 / (len as u64).max(1);2354        let coefficient: BalanceOf<T> = weight_saturation2355            .min(len_saturation)2356            .saturated_into::<BalanceOf<T>>();2357        final_fee2358            .saturating_mul(coefficient)2359            .saturated_into::<TransactionPriority>()2360    }23612362    fn withdraw_fee(2363        &self,2364        who: &T::AccountId,2365        call: &T::Call,2366        info: &DispatchInfoOf<T::Call>,2367        len: usize,2368	) -> Result<2369		(2370			BalanceOf<T>,2371			<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2372		),2373		TransactionValidityError,2374	> {2375        let tip = self.0;23762377        // Set fee based on call type. Creating collection costs 1 Unique.2378        // All other transactions have traditional fees so far2379        // let fee = match call.is_sub_type() {2380        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2381        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2382        //                                                 // _ => <BalanceOf<T>>::from(100)2383        // };2384        let fee = Self::traditional_fee(len, info, tip);23852386        // Only mess with balances if fee is not zero.2387        if fee.is_zero() {2388            return <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2389			.map(|i| (fee, i));2390        }23912392        // Determine who is paying transaction fee based on ecnomic model2393        // Parse call to extract collection ID and access collection sponsor2394        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2395            Some(Call::create_item(collection_id, _owner, _properties)) => {23962397                // sponsor timeout2398                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;23992400                let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;2401                let mut sponsored = true;2402                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2403                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2404                    let limit_time = last_tx_block + limit.into();2405                    if block_number <= limit_time {2406                        sponsored = false;2407                    }2408                }2409                if sponsored {2410                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2411                }24122413                // check free create limit2414                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2415                   (<Collection<T>>::get(collection_id).sponsor_confirmed) &&2416                   (sponsored)2417                {2418                    <Collection<T>>::get(collection_id).sponsor2419                } else {2420                    T::AccountId::default()2421                }2422            }2423            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2424                2425                let mut sponsor_transfer = false;2426                if <Collection<T>>::get(collection_id).sponsor_confirmed {24272428                    let collection_limits = <Collection<T>>::get(collection_id).limits;2429                    let collection_mode = <Collection<T>>::get(collection_id).mode;2430    2431                    // sponsor timeout2432                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2433                    sponsor_transfer = match collection_mode {2434                        CollectionMode::NFT => {2435    2436                            // get correct limit2437                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2438                                collection_limits.sponsor_transfer_timeout2439                            } else {2440                                ChainLimit::get().nft_sponsor_transfer_timeout2441                            };2442    2443                            let mut sponsored = true;2444                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2445                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2446                                let limit_time = last_tx_block + limit.into();2447                                if block_number <= limit_time {2448                                    sponsored = false;2449                                }2450                            }2451                            if sponsored {2452                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2453                            }24542455                            sponsored2456                        }2457                        CollectionMode::Fungible(_) => {2458    2459                            // get correct limit2460                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2461                                collection_limits.sponsor_transfer_timeout2462                            } else {2463                                ChainLimit::get().fungible_sponsor_transfer_timeout2464                            };2465    2466                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2467                            let mut sponsored = true;2468                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2469                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2470                                let limit_time = last_tx_block + limit.into();2471                                if block_number <= limit_time {2472                                    sponsored = false;2473                                }2474                            }2475                            if sponsored {2476                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2477                            }24782479                            sponsored2480                        }2481                        CollectionMode::ReFungible => {2482    2483                            // get correct limit2484                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2485                                collection_limits.sponsor_transfer_timeout2486                            } else {2487                                ChainLimit::get().refungible_sponsor_transfer_timeout2488                            };2489    2490                            let mut sponsored = true;2491                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2492                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2493                                let limit_time = last_tx_block + limit.into();2494                                if block_number <= limit_time {2495                                    sponsored = false;2496                                }2497                            }2498                            if sponsored {2499                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2500                            }25012502                            sponsored2503                        }2504                        _ => {2505                            false2506                        },2507                    };2508                }25092510                if !sponsor_transfer {2511                    T::AccountId::default()2512                } else {2513                    <Collection<T>>::get(collection_id).sponsor2514                }2515            }25162517            _ => T::AccountId::default(),2518        };25192520        // Sponsor smart contracts2521        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {25222523            // On instantiation: set the contract owner2524            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {25252526                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2527                    &who,2528                    code_hash,2529                    salt,2530                );2531                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());25322533                T::AccountId::default()2534            },25352536            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2537            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {25382539                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());25402541                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2542                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2543                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2544                  2545                if !owned_contract && white_list_enabled {2546                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2547                        return Err(InvalidTransaction::Call.into());2548                    }2549                }25502551                let mut sponsor_transfer = false;2552                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2553                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2554                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2555                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2556                    let limit_time = last_tx_block + rate_limit;25572558                    if block_number >= limit_time {2559                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2560                        sponsor_transfer = true;2561                    }2562                } else {2563                    sponsor_transfer = false;2564                }2565               2566                2567                let mut sp = T::AccountId::default();2568                if sponsor_transfer {2569                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2570                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2571                            sp = called_contract;2572                        }2573                    }2574                }25752576                sp2577            },25782579            _ => sponsor,2580        };25812582        let mut who_pays_fee: T::AccountId = sponsor.clone();2583        if sponsor == T::AccountId::default() {2584            who_pays_fee = who.clone();2585        }25862587		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2588			.map(|i| (fee, i))2589    }2590}259125922593impl<T: Config + Send + Sync> SignedExtension2594    for ChargeTransactionPayment<T>2595where2596    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2597    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2598    T::AccountId: AsRef<[u8]>,2599    T::AccountId: UncheckedFrom<T::Hash>,2600{2601    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2602    type AccountId = T::AccountId;2603    type Call = T::Call;2604    type AdditionalSigned = ();2605    type Pre = (2606        // tip2607        BalanceOf<T>,2608        // who pays fee2609        Self::AccountId,2610		// imbalance resulting from withdrawing the fee2611		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2612    );2613    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2614        Ok(())2615    }26162617    fn validate(2618        &self,2619        who: &Self::AccountId,2620        call: &Self::Call,2621        info: &DispatchInfoOf<Self::Call>,2622        len: usize,2623    ) -> TransactionValidity {2624		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2625		Ok(ValidTransaction {2626			priority: Self::get_priority(len, info, fee),2627			..Default::default()2628		})2629    }26302631    fn pre_dispatch(2632        self,2633        who: &Self::AccountId,2634        call: &Self::Call,2635        info: &DispatchInfoOf<Self::Call>,2636        len: usize,2637    ) -> Result<Self::Pre, TransactionValidityError> {2638        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2639        Ok((self.0, who.clone(), imbalance))2640    }26412642    fn post_dispatch(2643        pre: Self::Pre,2644        info: &DispatchInfoOf<Self::Call>,2645        post_info: &PostDispatchInfoOf<Self::Call>,2646        len: usize,2647        _result: &DispatchResult,2648    ) -> Result<(), TransactionValidityError> {2649		let (tip, who, imbalance) = pre;2650		let actual_fee = transaction_payment::Module::<T>::compute_actual_fee(2651			len as u32,2652			info,2653			post_info,2654			tip,2655		);2656		<T as transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2657		Ok(())2658    }2659}26602661// #endregion