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

difftreelog

source

pallets/nft/src/lib.rs99.1 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: ChainLimit::get().custom_data_limit, 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            let allowance_limit = if (1114                target_collection.limits.owner_can_transfer &&1115                Self::is_owner_or_admin_permissions(1116                    collection_id,1117                    sender.clone(),1118                )1119            ) {1120                None1121            } else if let Some(amount) = Self::owned_amount(1122                sender.clone(),1123                collection_id,1124                item_id,1125            ) {1126                Some(amount)1127            } else {1128                fail!(Error::<T>::NoPermission);1129            };11301131            if target_collection.access == AccessMode::WhiteList {1132                Self::check_white_list(collection_id, &sender)?;1133                Self::check_white_list(collection_id, &spender)?;1134            }11351136            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1137            let mut allowance: u128 = amount;1138            if allowance_exists {1139                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1140            }1141            if let Some(limit) = allowance_limit {1142                ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1143            }1144            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11451146            Ok(())1147        }1148        1149        /// 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.1150        /// 1151        /// # Permissions1152        /// * Collection Owner1153        /// * Collection Admin1154        /// * Current NFT owner1155        /// * Address approved by current NFT owner1156        /// 1157        /// # Arguments1158        /// 1159        /// * from: Address that owns token.1160        /// 1161        /// * recipient: Address of token recipient.1162        /// 1163        /// * collection_id.1164        /// 1165        /// * item_id: ID of the item.1166        /// 1167        /// * value: Amount to transfer.1168        #[weight = <T as Config>::WeightInfo::transfer_from()]1169        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11701171            let sender = ensure_signed(origin)?;1172            let mut appoved_transfer = false;11731174            // Check approval1175            let mut approval: u128 = 0;1176            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1177                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1178                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1179                appoved_transfer = true;1180            }11811182            let target_collection = <Collection<T>>::get(collection_id);11831184            // Limits check1185            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11861187            // Transfer permissions check         1188            ensure!(1189                appoved_transfer || 1190                (1191                    target_collection.limits.owner_can_transfer &&1192                    Self::is_owner_or_admin_permissions(collection_id, sender.clone())1193                ),1194                Error::<T>::NoPermission1195            );11961197            if target_collection.access == AccessMode::WhiteList {1198                Self::check_white_list(collection_id, &sender)?;1199                Self::check_white_list(collection_id, &recipient)?;1200            }12011202            // Reduce approval by transferred amount or remove if remaining approval drops to 01203            if approval.checked_sub(value).unwrap_or(0) > 0 {1204                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1205            }1206            else {1207                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1208            }12091210            match target_collection.mode1211            {1212                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1213                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1214                CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1215                _ => ()1216            };12171218            Ok(())1219        }12201221        // #[weight = 0]1222        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {12231224        //     // let no_perm_mes = "You do not have permissions to modify this collection";1225        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1226        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1227        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);12281229        //     // // on_nft_received  call12301231        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;12321233        //     Ok(())1234        // }12351236        /// Set off-chain data schema.1237        /// 1238        /// # Permissions1239        /// 1240        /// * Collection Owner1241        /// * Collection Admin1242        /// 1243        /// # Arguments1244        /// 1245        /// * collection_id.1246        /// 1247        /// * schema: String representing the offchain data schema.1248        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1249        pub fn set_variable_meta_data (1250            origin,1251            collection_id: CollectionId,1252            item_id: TokenId,1253            data: Vec<u8>1254        ) -> DispatchResult {1255            let sender = ensure_signed(origin)?;1256            1257            Self::collection_exists(collection_id)?;1258            Self::token_exists(collection_id, item_id, &sender)?;12591260            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12611262            // Modify permissions check1263            let target_collection = <Collection<T>>::get(collection_id);1264            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1265                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1266                Error::<T>::NoPermission);12671268            match target_collection.mode1269            {1270                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1271                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1272                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1273                _ => fail!(Error::<T>::UnexpectedCollectionType)1274            };12751276            Ok(())1277        }1278 1279        /// Set schema standard1280        /// ImageURL1281        /// Unique1282        /// 1283        /// # Permissions1284        /// 1285        /// * Collection Owner1286        /// * Collection Admin1287        /// 1288        /// # Arguments1289        /// 1290        /// * collection_id.1291        /// 1292        /// * schema: SchemaVersion: enum1293        #[weight = <T as Config>::WeightInfo::set_schema_version()]1294        pub fn set_schema_version(1295            origin,1296            collection_id: CollectionId,1297            version: SchemaVersion1298        ) -> DispatchResult {1299            let sender = ensure_signed(origin)?;1300            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1301            let mut target_collection = <Collection<T>>::get(collection_id);1302            target_collection.schema_version = version;1303            <Collection<T>>::insert(collection_id, target_collection);13041305            Ok(())1306        }13071308        /// Set off-chain data schema.1309        /// 1310        /// # Permissions1311        /// 1312        /// * Collection Owner1313        /// * Collection Admin1314        /// 1315        /// # Arguments1316        /// 1317        /// * collection_id.1318        /// 1319        /// * schema: String representing the offchain data schema.1320        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1321        pub fn set_offchain_schema(1322            origin,1323            collection_id: CollectionId,1324            schema: Vec<u8>1325        ) -> DispatchResult {1326            let sender = ensure_signed(origin)?;1327            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13281329            // check schema limit1330            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");13311332            let mut target_collection = <Collection<T>>::get(collection_id);1333            target_collection.offchain_schema = schema;1334            <Collection<T>>::insert(collection_id, target_collection);13351336            Ok(())1337        }13381339        /// Set const on-chain data schema.1340        /// 1341        /// # Permissions1342        /// 1343        /// * Collection Owner1344        /// * Collection Admin1345        /// 1346        /// # Arguments1347        /// 1348        /// * collection_id.1349        /// 1350        /// * schema: String representing the const on-chain data schema.1351        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1352        pub fn set_const_on_chain_schema (1353            origin,1354            collection_id: CollectionId,1355            schema: Vec<u8>1356        ) -> DispatchResult {1357            let sender = ensure_signed(origin)?;1358            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13591360            // check schema limit1361            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");13621363            let mut target_collection = <Collection<T>>::get(collection_id);1364            target_collection.const_on_chain_schema = schema;1365            <Collection<T>>::insert(collection_id, target_collection);13661367            Ok(())1368        }13691370        /// Set variable on-chain data schema.1371        /// 1372        /// # Permissions1373        /// 1374        /// * Collection Owner1375        /// * Collection Admin1376        /// 1377        /// # Arguments1378        /// 1379        /// * collection_id.1380        /// 1381        /// * schema: String representing the variable on-chain data schema.1382        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1383        pub fn set_variable_on_chain_schema (1384            origin,1385            collection_id: CollectionId,1386            schema: Vec<u8>1387        ) -> DispatchResult {1388            let sender = ensure_signed(origin)?;1389            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13901391            // check schema limit1392            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");13931394            let mut target_collection = <Collection<T>>::get(collection_id);1395            target_collection.variable_on_chain_schema = schema;1396            <Collection<T>>::insert(collection_id, target_collection);13971398            Ok(())1399        }14001401        // Sudo permissions function1402        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1403        pub fn set_chain_limits(1404            origin,1405            limits: ChainLimits1406        ) -> DispatchResult {14071408            #[cfg(not(feature = "runtime-benchmarks"))]1409            ensure_root(origin)?;14101411            <ChainLimit>::put(limits);1412            Ok(())1413        }14141415        /// Enable smart contract self-sponsoring.1416        /// 1417        /// # Permissions1418        /// 1419        /// * Contract Owner1420        /// 1421        /// # Arguments1422        /// 1423        /// * contract address1424        /// * enable flag1425        /// 1426        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1427        pub fn enable_contract_sponsoring(1428            origin,1429            contract_address: T::AccountId,1430            enable: bool1431        ) -> DispatchResult {14321433            let sender = ensure_signed(origin)?;14341435            #[cfg(feature = "runtime-benchmarks")]1436            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14371438            Self::ensure_contract_owned(sender, &contract_address)?;14391440            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1441            Ok(())1442        }14431444        /// Set the rate limit for contract sponsoring to specified number of blocks.1445        /// 1446        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1447        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1448        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1449        /// from contract endowment if there are at least B blocks between such transactions. 1450        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1451        /// 1452        /// # Permissions1453        /// 1454        /// * Contract Owner1455        /// 1456        /// # Arguments1457        /// 1458        /// -`contract_address`: Address of the contract to sponsor1459        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1460        /// 1461        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1462        pub fn set_contract_sponsoring_rate_limit(1463            origin,1464            contract_address: T::AccountId,1465            rate_limit: T::BlockNumber1466        ) -> DispatchResult {1467            let sender = ensure_signed(origin)?;14681469            #[cfg(feature = "runtime-benchmarks")]1470            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14711472            Self::ensure_contract_owned(sender, &contract_address)?;1473            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1474            Ok(())1475        }14761477        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1478        /// 1479        /// # Permissions1480        /// 1481        /// * Address that deployed smart contract.1482        /// 1483        /// # Arguments1484        /// 1485        /// -`contract_address`: Address of the contract.1486        /// 1487        /// - `enable`: .  1488        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1489        pub fn toggle_contract_white_list(1490            origin,1491            contract_address: T::AccountId,1492            enable: bool1493        ) -> DispatchResult {1494            let sender = ensure_signed(origin)?;14951496            #[cfg(feature = "runtime-benchmarks")]1497            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14981499            Self::ensure_contract_owned(sender, &contract_address)?;1500            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1501            Ok(())1502        }1503        1504        /// Add an address to smart contract white list.1505        /// 1506        /// # Permissions1507        /// 1508        /// * Address that deployed smart contract.1509        /// 1510        /// # Arguments1511        /// 1512        /// -`contract_address`: Address of the contract.1513        ///1514        /// -`account_address`: Address to add.1515        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1516        pub fn add_to_contract_white_list(1517            origin,1518            contract_address: T::AccountId,1519            account_address: T::AccountId1520        ) -> DispatchResult {1521            let sender = ensure_signed(origin)?;15221523            #[cfg(feature = "runtime-benchmarks")]1524            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1525            1526            Self::ensure_contract_owned(sender, &contract_address)?;      1527            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1528            Ok(())1529        }15301531        /// Remove an address from smart contract white list.1532        /// 1533        /// # Permissions1534        /// 1535        /// * Address that deployed smart contract.1536        /// 1537        /// # Arguments1538        /// 1539        /// -`contract_address`: Address of the contract.1540        ///1541        /// -`account_address`: Address to remove.1542        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1543        pub fn remove_from_contract_white_list(1544            origin,1545            contract_address: T::AccountId,1546            account_address: T::AccountId1547        ) -> DispatchResult {1548            let sender = ensure_signed(origin)?;15491550            #[cfg(feature = "runtime-benchmarks")]1551            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15521553            Self::ensure_contract_owned(sender, &contract_address)?;1554            <ContractWhiteList<T>>::remove(contract_address, account_address);1555            Ok(())1556        }15571558        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1559        pub fn set_collection_limits(1560            origin,1561            collection_id: u32,1562            new_limits: CollectionLimits,1563        ) -> DispatchResult {1564            let sender = ensure_signed(origin)?;1565            Self::check_owner_permissions(collection_id, sender.clone())?;1566            let mut target_collection = <Collection<T>>::get(collection_id);1567            let old_limits = target_collection.limits;1568            let chain_limits = ChainLimit::get();15691570            // collection bounds1571            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1572                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1573                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1574                Error::<T>::CollectionLimitBoundsExceeded);15751576            // token_limit   check  prev1577            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1578            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);15791580            ensure!(1581                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1582                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1583                Error::<T>::OwnerPermissionsCantBeReverted,1584            );15851586            target_collection.limits = new_limits;1587            <Collection<T>>::insert(collection_id, target_collection);15881589            Ok(())1590        } 1591    }1592}15931594impl<T: Config> Module<T> {15951596    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {15971598        let target_collection = <Collection<T>>::get(collection_id);15991600        // Limits check1601        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;16021603        // Transfer permissions check1604        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1605            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1606            Error::<T>::NoPermission);16071608        if target_collection.access == AccessMode::WhiteList {1609            Self::check_white_list(collection_id, &sender)?;1610            Self::check_white_list(collection_id, &recipient)?;1611        }16121613        match target_collection.mode1614        {1615            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient.clone())?,1616            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1617            CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient.clone())?,1618            _ => ()1619        };16201621        Self::deposit_event(RawEvent::Transfer(collection_id, item_id, sender, recipient, value));16221623        Ok(())1624    }162516261627    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {16281629        // check token limit and account token limit1630        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1631        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1632        1633        Ok(())1634    }16351636    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {16371638        // check token limit and account token limit1639        let total_items: u32 = ItemListIndex::get(collection_id);1640        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1641        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1642        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);16431644        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1645            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1646            Self::check_white_list(collection_id, owner)?;1647            Self::check_white_list(collection_id, sender)?;1648        }16491650        Ok(())1651    }16521653    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1654        match target_collection.mode1655        {1656            CollectionMode::NFT => {1657                if let CreateItemData::NFT(data) = data {1658                    // check sizes1659                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1660                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1661                } else {1662                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1663                }1664            },1665            CollectionMode::Fungible(_) => {1666                if let CreateItemData::Fungible(_) = data {1667                } else {1668                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1669                }1670            },1671            CollectionMode::ReFungible => {1672                if let CreateItemData::ReFungible(data) = data {16731674                    // check sizes1675                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1676                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16771678                    // Check refungibility limits1679                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1680                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1681                } else {1682                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1683                }1684            },1685            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1686        };16871688        Ok(())1689    }16901691    fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1692        match data1693        {1694            CreateItemData::NFT(data) => {1695                let item = NftItemType {1696                    owner: owner.clone(),1697                    const_data: data.const_data,1698                    variable_data: data.variable_data1699                };17001701                Self::add_nft_item(collection_id, item)?;1702            },1703            CreateItemData::Fungible(data) => {1704                Self::add_fungible_item(collection_id, &owner, data.value)?;1705            },1706            CreateItemData::ReFungible(data) => {1707                let mut owner_list = Vec::new();1708                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});17091710                let item = ReFungibleItemType {1711                    owner: owner_list,1712                    const_data: data.const_data,1713                    variable_data: data.variable_data1714                };17151716                Self::add_refungible_item(collection_id, item)?;1717            }1718        };17191720        // call event1721        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id), owner));17221723        Ok(())1724    }17251726    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {17271728        // Does new owner already have an account?1729        let mut balance: u128 = 0;1730        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1731            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1732        } 17331734        // Mint 1735        let item = FungibleItemType {1736            value: balance + value1737        };1738        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);17391740        // Update balance1741        let new_balance = <Balance<T>>::get(collection_id, owner)1742            .checked_add(value)1743            .ok_or(Error::<T>::NumOverflow)?;1744        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17451746        Ok(())1747    }17481749    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1750        let current_index = <ItemListIndex>::get(collection_id)1751            .checked_add(1)1752            .ok_or(Error::<T>::NumOverflow)?;1753        let itemcopy = item.clone();17541755        let value = item.owner.first().unwrap().fraction;1756        let owner = item.owner.first().unwrap().owner.clone();17571758        Self::add_token_index(collection_id, current_index, &owner)?;17591760        <ItemListIndex>::insert(collection_id, current_index);1761        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17621763        // Update balance1764        let new_balance = <Balance<T>>::get(collection_id, &owner)1765            .checked_add(value)1766            .ok_or(Error::<T>::NumOverflow)?;1767        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17681769        Ok(())1770    }17711772    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1773        let current_index = <ItemListIndex>::get(collection_id)1774            .checked_add(1)1775            .ok_or(Error::<T>::NumOverflow)?;17761777        let item_owner = item.owner.clone();1778        Self::add_token_index(collection_id, current_index, &item.owner)?;17791780        <ItemListIndex>::insert(collection_id, current_index);1781        <NftItemList<T>>::insert(collection_id, current_index, item);17821783        // Update balance1784        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1785            .checked_add(1)1786            .ok_or(Error::<T>::NumOverflow)?;1787        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17881789        Ok(())1790    }17911792    fn burn_refungible_item(1793        collection_id: CollectionId,1794        item_id: TokenId,1795        owner: &T::AccountId,1796    ) -> DispatchResult {1797        ensure!(1798            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1799            Error::<T>::TokenNotFound1800        );1801        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1802        let rft_balance = token1803            .owner1804            .iter()1805            .filter(|&i| i.owner == *owner)1806            .next()1807            .unwrap();1808        Self::remove_token_index(collection_id, item_id, owner)?;18091810        // update balance1811        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1812            .checked_sub(rft_balance.fraction)1813            .ok_or(Error::<T>::NumOverflow)?;1814        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);18151816        // Re-create owners list with sender removed1817        let index = token1818            .owner1819            .iter()1820            .position(|i| i.owner == *owner)1821            .unwrap();1822        token.owner.remove(index);1823        let owner_count = token.owner.len();18241825        // Burn the token completely if this was the last (only) owner1826        if owner_count == 0 {1827            <ReFungibleItemList<T>>::remove(collection_id, item_id);1828        }1829        else {1830            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1831        }18321833        Ok(())1834    }18351836    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1837        ensure!(1838            <NftItemList<T>>::contains_key(collection_id, item_id),1839            Error::<T>::TokenNotFound1840        );1841        let item = <NftItemList<T>>::get(collection_id, item_id);1842        Self::remove_token_index(collection_id, item_id, &item.owner)?;18431844        // update balance1845        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1846            .checked_sub(1)1847            .ok_or(Error::<T>::NumOverflow)?;1848        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1849        <NftItemList<T>>::remove(collection_id, item_id);18501851        Ok(())1852    }18531854    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1855        ensure!(1856            <FungibleItemList<T>>::contains_key(collection_id, owner),1857            Error::<T>::TokenNotFound1858        );1859        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1860        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18611862        // update balance1863        let new_balance = <Balance<T>>::get(collection_id, owner)1864            .checked_sub(value)1865            .ok_or(Error::<T>::NumOverflow)?;1866        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18671868        if balance.value - value > 0 {1869            balance.value -= value;1870            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1871        }1872        else {1873            <FungibleItemList<T>>::remove(collection_id, owner);1874        }18751876        Ok(())1877    }18781879    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1880        ensure!(1881            <Collection<T>>::contains_key(collection_id),1882            Error::<T>::CollectionNotFound1883        );1884        Ok(())1885    }18861887    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1888        Self::collection_exists(collection_id)?;18891890        let target_collection = <Collection<T>>::get(collection_id);1891        ensure!(1892            subject == target_collection.owner,1893            Error::<T>::NoPermission1894        );18951896        Ok(())1897    }18981899    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1900        let target_collection = <Collection<T>>::get(collection_id);1901        let mut result: bool = subject == target_collection.owner;1902        let exists = <AdminList<T>>::contains_key(collection_id);19031904        if !result & exists {1905            if <AdminList<T>>::get(collection_id).contains(&subject) {1906                result = true1907            }1908        }19091910        result1911    }19121913    fn check_owner_or_admin_permissions(1914        collection_id: CollectionId,1915        subject: T::AccountId,1916    ) -> DispatchResult {1917        Self::collection_exists(collection_id)?;1918        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());19191920        ensure!(1921            result,1922            Error::<T>::NoPermission1923        );1924        Ok(())1925    }19261927    fn owned_amount(1928        subject: T::AccountId,1929        collection_id: CollectionId,1930        item_id: TokenId,1931    ) -> Option<u128> {1932        let target_collection = <Collection<T>>::get(collection_id);19331934        match target_collection.mode {1935            CollectionMode::NFT => {1936                if <NftItemList<T>>::get(collection_id, item_id).owner == subject {1937                    return Some(1)1938                }1939                None1940            },1941            CollectionMode::Fungible(_) => {1942                if <FungibleItemList<T>>::contains_key(collection_id, &subject) {1943                    return Some(<FungibleItemList<T>>::get(collection_id, &subject)1944                        .value);1945                }1946                None1947            },1948            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)1949                .owner1950                .iter()1951                .find(|i| i.owner == subject)1952                .map(|i| i.fraction),1953            CollectionMode::Invalid => None,1954        }1955    }19561957    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1958        let target_collection = <Collection<T>>::get(collection_id);19591960        match target_collection.mode {1961            CollectionMode::NFT => {1962                <NftItemList<T>>::get(collection_id, item_id).owner == subject1963            }1964            CollectionMode::Fungible(_) => {1965                <FungibleItemList<T>>::contains_key(collection_id, &subject)1966            }1967            CollectionMode::ReFungible => {1968                <ReFungibleItemList<T>>::get(collection_id, item_id)1969                    .owner1970                    .iter()1971                    .any(|i| i.owner == subject)1972            }1973            CollectionMode::Invalid => false,1974        }1975    }19761977    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1978        let mes = Error::<T>::AddresNotInWhiteList;1979        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);19801981        Ok(())1982    }19831984    /// Check if token exists. In case of Fungible, check if there is an entry for 1985    /// the owner in fungible balances double map1986    fn token_exists(1987        collection_id: CollectionId,1988        item_id: TokenId,1989        owner: &T::AccountId1990    ) -> DispatchResult {1991        let target_collection = <Collection<T>>::get(collection_id);1992        let exists = match target_collection.mode1993        {1994            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1995            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),1996            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1997            _ => false1998        };19992000        ensure!(exists == true, Error::<T>::TokenNotFound);2001        Ok(())2002    }20032004    fn transfer_fungible(2005        collection_id: CollectionId,2006        value: u128,2007        owner: &T::AccountId,2008        recipient: &T::AccountId,2009    ) -> DispatchResult {2010        Self::token_exists(collection_id, 0, owner)?;20112012        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);2013        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);20142015        // Send balance to recipient (updates balanceOf of recipient)2016        Self::add_fungible_item(collection_id, recipient, value)?;20172018        // update balanceOf of sender2019        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);20202021        // Reduce or remove sender2022        if balance.value == value {2023            <FungibleItemList<T>>::remove(collection_id, owner);2024        }2025        else {2026            balance.value -= value;2027            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);2028        }20292030        Ok(())2031    }20322033    fn transfer_refungible(2034        collection_id: CollectionId,2035        item_id: TokenId,2036        value: u128,2037        owner: T::AccountId,2038        new_owner: T::AccountId,2039    ) -> DispatchResult {2040        Self::token_exists(collection_id, item_id, &owner)?;20412042        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);2043        let item = full_item2044            .owner2045            .iter()2046            .filter(|i| i.owner == owner)2047            .next()2048            .ok_or(Error::<T>::NumOverflow)?;2049        let amount = item.fraction;20502051        ensure!(amount >= value, Error::<T>::TokenValueTooLow);20522053        // update balance2054        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2055            .checked_sub(value)2056            .ok_or(Error::<T>::NumOverflow)?;2057        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20582059        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2060            .checked_add(value)2061            .ok_or(Error::<T>::NumOverflow)?;2062        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20632064        let old_owner = item.owner.clone();2065        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20662067        // transfer2068        if amount == value && !new_owner_has_account {2069            // change owner2070            // new owner do not have account2071            let mut new_full_item = full_item.clone();2072            new_full_item2073                .owner2074                .iter_mut()2075                .find(|i| i.owner == owner)2076                .unwrap()2077                .owner = new_owner.clone();2078            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20792080            // update index collection2081            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2082        } else {2083            let mut new_full_item = full_item.clone();2084            new_full_item2085                .owner2086                .iter_mut()2087                .find(|i| i.owner == owner)2088                .unwrap()2089                .fraction -= value;20902091            // separate amount2092            if new_owner_has_account {2093                // new owner has account2094                new_full_item2095                    .owner2096                    .iter_mut()2097                    .find(|i| i.owner == new_owner)2098                    .unwrap()2099                    .fraction += value;2100            } else {2101                // new owner do not have account2102                new_full_item.owner.push(Ownership {2103                    owner: new_owner.clone(),2104                    fraction: value,2105                });2106                Self::add_token_index(collection_id, item_id, &new_owner)?;2107            }21082109            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2110        }21112112        Ok(())2113    }21142115    fn transfer_nft(2116        collection_id: CollectionId,2117        item_id: TokenId,2118        sender: T::AccountId,2119        new_owner: T::AccountId,2120    ) -> DispatchResult {2121        Self::token_exists(collection_id, item_id, &sender)?;21222123        let mut item = <NftItemList<T>>::get(collection_id, item_id);21242125        ensure!(2126            sender == item.owner,2127            Error::<T>::MustBeTokenOwner2128        );21292130        // update balance2131        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2132            .checked_sub(1)2133            .ok_or(Error::<T>::NumOverflow)?;2134        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);21352136        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2137            .checked_add(1)2138            .ok_or(Error::<T>::NumOverflow)?;2139        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);21402141        // change owner2142        let old_owner = item.owner.clone();2143        item.owner = new_owner.clone();2144        <NftItemList<T>>::insert(collection_id, item_id, item);21452146        // update index collection2147        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21482149        Ok(())2150    }2151    2152    fn set_re_fungible_variable_data(2153        collection_id: CollectionId,2154        item_id: TokenId,2155        data: Vec<u8>2156    ) -> DispatchResult {2157        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);21582159        item.variable_data = data;21602161        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21622163        Ok(())2164    }21652166    fn set_nft_variable_data(2167        collection_id: CollectionId,2168        item_id: TokenId,2169        data: Vec<u8>2170    ) -> DispatchResult {2171        let mut item = <NftItemList<T>>::get(collection_id, item_id);2172        2173        item.variable_data = data;21742175        <NftItemList<T>>::insert(collection_id, item_id, item);2176        2177        Ok(())2178    }21792180    fn init_collection(item: &CollectionType<T::AccountId>) {2181        // check params2182        assert!(2183            item.decimal_points <= MAX_DECIMAL_POINTS,2184            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2185        );2186        assert!(2187            item.name.len() <= 64,2188            "Collection name can not be longer than 63 char"2189        );2190        assert!(2191            item.name.len() <= 256,2192            "Collection description can not be longer than 255 char"2193        );2194        assert!(2195            item.token_prefix.len() <= 16,2196            "Token prefix can not be longer than 15 char"2197        );21982199        // Generate next collection ID2200        let next_id = CreatedCollectionCount::get()2201            .checked_add(1)2202            .unwrap();22032204        CreatedCollectionCount::put(next_id);2205    }22062207    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2208        let current_index = <ItemListIndex>::get(collection_id)2209            .checked_add(1)2210            .unwrap();22112212        let item_owner = item.owner.clone();2213        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22142215        <ItemListIndex>::insert(collection_id, current_index);22162217        // Update balance2218        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2219            .checked_add(1)2220            .unwrap();2221        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2222    }22232224    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2225        let current_index = <ItemListIndex>::get(collection_id)2226            .checked_add(1)2227            .unwrap();22282229        Self::add_token_index(collection_id, current_index, owner).unwrap();22302231        <ItemListIndex>::insert(collection_id, current_index);22322233        // Update balance2234        let new_balance = <Balance<T>>::get(collection_id, owner)2235            .checked_add(item.value)2236            .unwrap();2237        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2238    }22392240    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2241        let current_index = <ItemListIndex>::get(collection_id)2242            .checked_add(1)2243            .unwrap();22442245        let value = item.owner.first().unwrap().fraction;2246        let owner = item.owner.first().unwrap().owner.clone();22472248        Self::add_token_index(collection_id, current_index, &owner).unwrap();22492250        <ItemListIndex>::insert(collection_id, current_index);22512252        // Update balance2253        let new_balance = <Balance<T>>::get(collection_id, &owner)2254            .checked_add(value)2255            .unwrap();2256        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2257    }22582259    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {22602261        // add to account limit2262        if <AccountItemCount<T>>::contains_key(owner) {22632264            // bound Owned tokens by a single address2265            let count = <AccountItemCount<T>>::get(owner);2266            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);22672268            <AccountItemCount<T>>::insert(owner.clone(), count2269                .checked_add(1)2270                .ok_or(Error::<T>::NumOverflow)?);2271        }2272        else {2273            <AccountItemCount<T>>::insert(owner.clone(), 1);2274        }22752276        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2277        if list_exists {2278            let mut list = <AddressTokens<T>>::get(collection_id, owner);2279            let item_contains = list.contains(&item_index.clone());22802281            if !item_contains {2282                list.push(item_index.clone());2283            }22842285            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2286        } else {2287            let mut itm = Vec::new();2288            itm.push(item_index.clone());2289            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2290        }22912292        Ok(())2293    }22942295    fn remove_token_index(2296        collection_id: CollectionId,2297        item_index: TokenId,2298        owner: &T::AccountId,2299    ) -> DispatchResult {23002301        // update counter2302        <AccountItemCount<T>>::insert(owner.clone(), 2303            <AccountItemCount<T>>::get(owner)2304            .checked_sub(1)2305            .ok_or(Error::<T>::NumOverflow)?);230623072308        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2309        if list_exists {2310            let mut list = <AddressTokens<T>>::get(collection_id, owner);2311            let item_contains = list.contains(&item_index.clone());23122313            if item_contains {2314                list.retain(|&item| item != item_index);2315                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2316            }2317        }23182319        Ok(())2320    }23212322    fn move_token_index(2323        collection_id: CollectionId,2324        item_index: TokenId,2325        old_owner: &T::AccountId,2326        new_owner: &T::AccountId,2327    ) -> DispatchResult {2328        Self::remove_token_index(collection_id, item_index, old_owner)?;2329        Self::add_token_index(collection_id, item_index, new_owner)?;23302331        Ok(())2332    }2333    2334    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2335        if <ContractOwner<T>>::contains_key(contract.clone()) {2336            let owner = <ContractOwner<T>>::get(contract);2337            ensure!(account == owner, Error::<T>::NoPermission);2338        } else {2339            fail!(Error::<T>::NoPermission);2340        }23412342        Ok(())2343    }2344}23452346////////////////////////////////////////////////////////////////////////////////////////////////////2347// Economic models2348// #region23492350/// Fee multiplier.2351pub type Multiplier = FixedU128;23522353type BalanceOf<T> = <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::Balance;23542355/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2356/// in the queue.2357#[derive(Encode, Decode, Clone, Eq, PartialEq)]2358pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);23592360impl<T: Config + Send + Sync> sp_std::fmt::Debug 2361    for ChargeTransactionPayment<T>2362{2363	#[cfg(feature = "std")]2364	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2365		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2366	}2367	#[cfg(not(feature = "std"))]2368	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2369		Ok(())2370	}2371}23722373impl<T: Config> ChargeTransactionPayment<T>2374where2375    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2376    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2377    T::AccountId: AsRef<[u8]>,2378    T::AccountId: UncheckedFrom<T::Hash>,2379{2380    fn traditional_fee(2381        len: usize,2382        info: &DispatchInfoOf<T::Call>,2383        tip: BalanceOf<T>,2384    ) -> BalanceOf<T>2385    where2386        T::Call: Dispatchable<Info = DispatchInfo>,2387    {2388        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2389    }23902391	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2392        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2393        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2394        let len_saturation = max_block_length as u64 / (len as u64).max(1);2395        let coefficient: BalanceOf<T> = weight_saturation2396            .min(len_saturation)2397            .saturated_into::<BalanceOf<T>>();2398        final_fee2399            .saturating_mul(coefficient)2400            .saturated_into::<TransactionPriority>()2401    }24022403    fn withdraw_fee(2404        &self,2405        who: &T::AccountId,2406        call: &T::Call,2407        info: &DispatchInfoOf<T::Call>,2408        len: usize,2409	) -> Result<2410		(2411			BalanceOf<T>,2412			<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2413		),2414		TransactionValidityError,2415	> {2416        let tip = self.0;24172418        // Set fee based on call type. Creating collection costs 1 Unique.2419        // All other transactions have traditional fees so far2420        // let fee = match call.is_sub_type() {2421        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2422        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2423        //                                                 // _ => <BalanceOf<T>>::from(100)2424        // };2425        let fee = Self::traditional_fee(len, info, tip);24262427        // Only mess with balances if fee is not zero.2428        if fee.is_zero() {2429            return <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2430			.map(|i| (fee, i));2431        }24322433        // Determine who is paying transaction fee based on ecnomic model2434        // Parse call to extract collection ID and access collection sponsor2435        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2436            Some(Call::create_item(collection_id, _owner, _properties)) => {24372438                // sponsor timeout2439                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;24402441                let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;2442                let mut sponsored = true;2443                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2444                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2445                    let limit_time = last_tx_block + limit.into();2446                    if block_number <= limit_time {2447                        sponsored = false;2448                    }2449                }2450                if sponsored {2451                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2452                }24532454                // check free create limit2455                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2456                   (<Collection<T>>::get(collection_id).sponsor_confirmed) &&2457                   (sponsored)2458                {2459                    <Collection<T>>::get(collection_id).sponsor2460                } else {2461                    T::AccountId::default()2462                }2463            }2464            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2465                2466                let mut sponsor_transfer = false;2467                if <Collection<T>>::get(collection_id).sponsor_confirmed {24682469                    let collection_limits = <Collection<T>>::get(collection_id).limits;2470                    let collection_mode = <Collection<T>>::get(collection_id).mode;2471    2472                    // sponsor timeout2473                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2474                    sponsor_transfer = match collection_mode {2475                        CollectionMode::NFT => {2476    2477                            // get correct limit2478                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2479                                collection_limits.sponsor_transfer_timeout2480                            } else {2481                                ChainLimit::get().nft_sponsor_transfer_timeout2482                            };2483    2484                            let mut sponsored = true;2485                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2486                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2487                                let limit_time = last_tx_block + limit.into();2488                                if block_number <= limit_time {2489                                    sponsored = false;2490                                }2491                            }2492                            if sponsored {2493                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2494                            }24952496                            sponsored2497                        }2498                        CollectionMode::Fungible(_) => {2499    2500                            // get correct limit2501                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2502                                collection_limits.sponsor_transfer_timeout2503                            } else {2504                                ChainLimit::get().fungible_sponsor_transfer_timeout2505                            };2506    2507                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2508                            let mut sponsored = true;2509                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2510                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2511                                let limit_time = last_tx_block + limit.into();2512                                if block_number <= limit_time {2513                                    sponsored = false;2514                                }2515                            }2516                            if sponsored {2517                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2518                            }25192520                            sponsored2521                        }2522                        CollectionMode::ReFungible => {2523    2524                            // get correct limit2525                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2526                                collection_limits.sponsor_transfer_timeout2527                            } else {2528                                ChainLimit::get().refungible_sponsor_transfer_timeout2529                            };2530    2531                            let mut sponsored = true;2532                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2533                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2534                                let limit_time = last_tx_block + limit.into();2535                                if block_number <= limit_time {2536                                    sponsored = false;2537                                }2538                            }2539                            if sponsored {2540                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2541                            }25422543                            sponsored2544                        }2545                        _ => {2546                            false2547                        },2548                    };2549                }25502551                if !sponsor_transfer {2552                    T::AccountId::default()2553                } else {2554                    <Collection<T>>::get(collection_id).sponsor2555                }2556            }25572558            _ => T::AccountId::default(),2559        };25602561        // Sponsor smart contracts2562        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {25632564            // On instantiation: set the contract owner2565            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {25662567                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2568                    &who,2569                    code_hash,2570                    salt,2571                );2572                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());25732574                T::AccountId::default()2575            },25762577            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2578            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {25792580                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());25812582                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2583                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2584                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2585                  2586                if !owned_contract && white_list_enabled {2587                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2588                        return Err(InvalidTransaction::Call.into());2589                    }2590                }25912592                let mut sponsor_transfer = false;2593                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2594                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2595                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2596                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2597                    let limit_time = last_tx_block + rate_limit;25982599                    if block_number >= limit_time {2600                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2601                        sponsor_transfer = true;2602                    }2603                } else {2604                    sponsor_transfer = false;2605                }2606               2607                2608                let mut sp = T::AccountId::default();2609                if sponsor_transfer {2610                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2611                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2612                            sp = called_contract;2613                        }2614                    }2615                }26162617                sp2618            },26192620            _ => sponsor,2621        };26222623        let mut who_pays_fee: T::AccountId = sponsor.clone();2624        if sponsor == T::AccountId::default() {2625            who_pays_fee = who.clone();2626        }26272628		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2629			.map(|i| (fee, i))2630    }2631}263226332634impl<T: Config + Send + Sync> SignedExtension2635    for ChargeTransactionPayment<T>2636where2637    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2638    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2639    T::AccountId: AsRef<[u8]>,2640    T::AccountId: UncheckedFrom<T::Hash>,2641{2642    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2643    type AccountId = T::AccountId;2644    type Call = T::Call;2645    type AdditionalSigned = ();2646    type Pre = (2647        // tip2648        BalanceOf<T>,2649        // who pays fee2650        Self::AccountId,2651		// imbalance resulting from withdrawing the fee2652		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2653    );2654    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2655        Ok(())2656    }26572658    fn validate(2659        &self,2660        who: &Self::AccountId,2661        call: &Self::Call,2662        info: &DispatchInfoOf<Self::Call>,2663        len: usize,2664    ) -> TransactionValidity {2665		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2666		Ok(ValidTransaction {2667			priority: Self::get_priority(len, info, fee),2668			..Default::default()2669		})2670    }26712672    fn pre_dispatch(2673        self,2674        who: &Self::AccountId,2675        call: &Self::Call,2676        info: &DispatchInfoOf<Self::Call>,2677        len: usize,2678    ) -> Result<Self::Pre, TransactionValidityError> {2679        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2680        Ok((self.0, who.clone(), imbalance))2681    }26822683    fn post_dispatch(2684        pre: Self::Pre,2685        info: &DispatchInfoOf<Self::Call>,2686        post_info: &PostDispatchInfoOf<Self::Call>,2687        len: usize,2688        _result: &DispatchResult,2689    ) -> Result<(), TransactionValidityError> {2690		let (tip, who, imbalance) = pre;2691		let actual_fee = transaction_payment::Module::<T>::compute_actual_fee(2692			len as u32,2693			info,2694			post_info,2695			tip,2696		);2697		<T as transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2698		Ok(())2699    }2700}27012702// #endregion