git.delta.rocks / unique-network / refs/commits / 1ddda31cdfab

difftreelog

source

pallets/nft/src/lib.rs96.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: u32::max_value(), 198            sponsor_transfer_timeout: 14400,199            owner_can_transfer: true,200            owner_can_destroy: true201        }202    }203}204205#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]206#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]207pub struct ChainLimits {208    pub collection_numbers_limit: u32,209    pub account_token_ownership_limit: u32,210    pub collections_admins_limit: u64,211    pub custom_data_limit: u32,212213    // Timeouts for item types in passed blocks214    pub nft_sponsor_transfer_timeout: u32,215    pub fungible_sponsor_transfer_timeout: u32,216    pub refungible_sponsor_transfer_timeout: u32,217218    // Schema limits219    pub offchain_schema_limit: u32,220    pub variable_on_chain_schema_limit: u32,221    pub const_on_chain_schema_limit: u32,222}223224pub trait WeightInfo {225	fn create_collection() -> Weight;226	fn destroy_collection() -> Weight;227	fn add_to_white_list() -> Weight;228	fn remove_from_white_list() -> Weight;229    fn set_public_access_mode() -> Weight;230    fn set_mint_permission() -> Weight;231    fn change_collection_owner() -> Weight;232    fn add_collection_admin() -> Weight;233    fn remove_collection_admin() -> Weight;234    fn set_collection_sponsor() -> Weight;235    fn confirm_sponsorship() -> Weight;236    fn remove_collection_sponsor() -> Weight;237    fn create_item(s: usize) -> Weight;238    fn burn_item() -> Weight;239    fn transfer() -> Weight;240    fn approve() -> Weight;241    fn transfer_from() -> Weight;242    fn set_offchain_schema() -> Weight;243    fn set_const_on_chain_schema() -> Weight;244    fn set_variable_on_chain_schema() -> Weight;245    fn set_variable_meta_data() -> Weight;246    fn enable_contract_sponsoring() -> Weight;247    fn set_schema_version() -> Weight;248    fn set_chain_limits() -> Weight;249    fn set_contract_sponsoring_rate_limit() -> Weight;250    fn toggle_contract_white_list() -> Weight;251    fn add_to_contract_white_list() -> Weight;252    fn remove_from_contract_white_list() -> Weight;253    fn set_collection_limits() -> Weight;254}255256#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]257#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]258pub struct CreateNftData {259    pub const_data: Vec<u8>,260    pub variable_data: Vec<u8>,261}262263#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]264#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]265pub struct CreateFungibleData {266    pub value: u128,267}268269#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]270#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]271pub struct CreateReFungibleData {272    pub const_data: Vec<u8>,273    pub variable_data: Vec<u8>,274    pub pieces: u128,275}276277#[derive(Encode, Decode, Debug, Clone, PartialEq)]278#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]279pub enum CreateItemData {280    NFT(CreateNftData),281    Fungible(CreateFungibleData),282    ReFungible(CreateReFungibleData),283}284285impl CreateItemData {286    pub fn len(&self) -> usize {287        let len = match self {288            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),289            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),290            _ => 0291        };292        293        return len;294    }295}296297impl From<CreateNftData> for CreateItemData {298    fn from(item: CreateNftData) -> Self {299        CreateItemData::NFT(item)300    }301}302303impl From<CreateReFungibleData> for CreateItemData {304    fn from(item: CreateReFungibleData) -> Self {305        CreateItemData::ReFungible(item)306    }307}308309impl From<CreateFungibleData> for CreateItemData {310    fn from(item: CreateFungibleData) -> Self {311        CreateItemData::Fungible(item)312    }313}314315316decl_error! {317	/// Error for non-fungible-token module.318	pub enum Error for Module<T: Config> {319        /// Total collections bound exceeded.320        TotalCollectionsLimitExceeded,321		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.322        CollectionDecimalPointLimitExceeded, 323        /// Collection name can not be longer than 63 char.324        CollectionNameLimitExceeded, 325        /// Collection description can not be longer than 255 char.326        CollectionDescriptionLimitExceeded, 327        /// Token prefix can not be longer than 15 char.328        CollectionTokenPrefixLimitExceeded,329        /// This collection does not exist.330        CollectionNotFound,331        /// Item not exists.332        TokenNotFound,333        /// Admin not found334        AdminNotFound,335        /// Arithmetic calculation overflow.336        NumOverflow,       337        /// Account already has admin role.338        AlreadyAdmin,  339        /// You do not own this collection.340        NoPermission,341        /// This address is not set as sponsor, use setCollectionSponsor first.342        ConfirmUnsetSponsorFail,343        /// Collection is not in mint mode.344        PublicMintingNotAllowed,345        /// Sender parameter and item owner must be equal.346        MustBeTokenOwner,347        /// Item balance not enough.348        TokenValueTooLow,349        /// Size of item is too large.350        NftSizeLimitExceeded,351        /// No approve found352        ApproveNotFound,353        /// Requested value more than approved.354        TokenValueNotEnough,355        /// Only approved addresses can call this method.356        ApproveRequired,357        /// Address is not in white list.358        AddresNotInWhiteList,359        /// Number of collection admins bound exceeded.360        CollectionAdminsLimitExceeded,361        /// Owned tokens by a single address bound exceeded.362        AddressOwnershipLimitExceeded,363        /// Length of items properties must be greater than 0.364        EmptyArgument,365        /// const_data exceeded data limit.366        TokenConstDataLimitExceeded,367        /// variable_data exceeded data limit.368        TokenVariableDataLimitExceeded,369        /// Not NFT item data used to mint in NFT collection.370        NotNftDataUsedToMintNftCollectionToken,371        /// Not Fungible item data used to mint in Fungible collection.372        NotFungibleDataUsedToMintFungibleCollectionToken,373        /// Not Re Fungible item data used to mint in Re Fungible collection.374        NotReFungibleDataUsedToMintReFungibleCollectionToken,375        /// Unexpected collection type.376        UnexpectedCollectionType,377        /// Can't store metadata in fungible tokens.378        CantStoreMetadataInFungibleTokens,379        /// Collection token limit exceeded380        CollectionTokenLimitExceeded,381        /// Account token limit exceeded per collection382        AccountTokenLimitExceeded,383        /// Collection limit bounds per collection exceeded384        CollectionLimitBoundsExceeded,385        /// Schema data size limit bound exceeded386        SchemaDataLimitExceeded,387        /// Maximum refungibility exceeded388        WrongRefungiblePieces389	}390}391392pub trait Config: system::Config + Sized + transaction_payment::Config + pallet_contracts::Config {393    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;394395    /// Weight information for extrinsics in this pallet.396	type WeightInfo: WeightInfo;397}398399#[cfg(feature = "runtime-benchmarks")]400mod benchmarking;401402// #endregion403404decl_storage! {405    trait Store for Module<T: Config> as Nft {406407        // Private members408        NextCollectionID: CollectionId;409        CreatedCollectionCount: u32;410        ChainVersion: u64;411        ItemListIndex: map hasher(identity) CollectionId => TokenId;412413        // Chain limits struct414        pub ChainLimit get(fn chain_limit) config(): ChainLimits;415416        // Bound counters417        CollectionCount: u32;418        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;419420        // Basic collections421        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;422        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;423        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;424425        /// Balance owner per collection map426        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;427428        /// second parameter: item id + owner account id + spender account id429        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;430431        /// Item collections432        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;433        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;434        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;435436        /// Index list437        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;438439        /// Tokens transfer baskets440        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;441        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;442        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;443        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;444445        // Contract Sponsorship and Ownership446        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;447        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;448        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;449        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;450        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 451        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 452    }453    add_extra_genesis {454        build(|config: &GenesisConfig<T>| {455            // Modification of storage456            for (_num, _c) in &config.collection {457                <Module<T>>::init_collection(_c);458            }459460            for (_num, _c, _i) in &config.nft_item_id {461                <Module<T>>::init_nft_token(*_c, _i);462            }463464            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {465                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);466            }467468            for (_num, _c, _i) in &config.refungible_item_id {469                <Module<T>>::init_refungible_token(*_c, _i);470            }471        })472    }473}474475decl_event!(476    pub enum Event<T>477    where478        AccountId = <T as system::Config>::AccountId,479    {480        /// New collection was created481        /// 482        /// # Arguments483        /// 484        /// * collection_id: Globally unique identifier of newly created collection.485        /// 486        /// * mode: [CollectionMode] converted into u8.487        /// 488        /// * account_id: Collection owner.489        Created(CollectionId, u8, AccountId),490491        /// New item was created.492        /// 493        /// # Arguments494        /// 495        /// * collection_id: Id of the collection where item was created.496        /// 497        /// * item_id: Id of an item. Unique within the collection.498        ItemCreated(CollectionId, TokenId),499500        /// Collection item was burned.501        /// 502        /// # Arguments503        /// 504        /// collection_id.505        /// 506        /// item_id: Identifier of burned NFT.507        ItemDestroyed(CollectionId, TokenId),508    }509);510511decl_module! {512    pub struct Module<T: Config> for enum Call 513    where 514        origin: T::Origin515    {516        fn deposit_event() = default;517        type Error = Error<T>;518519        fn on_initialize(now: T::BlockNumber) -> Weight {520521            if ChainVersion::get() < 2522            {523                let value = NextCollectionID::get();524                CreatedCollectionCount::put(value);525                ChainVersion::put(2);526            }527528            0529        }530531        /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.532        /// 533        /// # Permissions534        /// 535        /// * Anyone.536        /// 537        /// # Arguments538        /// 539        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.540        /// 541        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.542        /// 543        /// * token_prefix: UTF-8 string with token prefix.544        /// 545        /// * mode: [CollectionMode] collection type and type dependent data.546        // returns collection ID547        #[weight = <T as Config>::WeightInfo::create_collection()]548        pub fn create_collection(origin,549                                 collection_name: Vec<u16>,550                                 collection_description: Vec<u16>,551                                 token_prefix: Vec<u8>,552                                 mode: CollectionMode) -> DispatchResult {553554            // Anyone can create a collection555            let who = ensure_signed(origin)?;556557            let decimal_points = match mode {558                CollectionMode::Fungible(points) => points,559                _ => 0560            };561562            // bound Total number of collections563            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);564565            // check params566            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);567            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);568            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);569            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);570571            // Generate next collection ID572            let next_id = CreatedCollectionCount::get()573                .checked_add(1)574                .ok_or(Error::<T>::NumOverflow)?;575576            // bound counter577            let total = CollectionCount::get()578                .checked_add(1)579                .ok_or(Error::<T>::NumOverflow)?;580581            CreatedCollectionCount::put(next_id);582            CollectionCount::put(total);583584            // Create new collection585            let new_collection = CollectionType {586                owner: who.clone(),587                name: collection_name,588                mode: mode.clone(),589                mint_mode: false,590                access: AccessMode::Normal,591                description: collection_description,592                decimal_points: decimal_points,593                token_prefix: token_prefix,594                offchain_schema: Vec::new(),595                schema_version: SchemaVersion::ImageURL,596                sponsor: T::AccountId::default(),597                sponsor_confirmed: false,598                variable_on_chain_schema: Vec::new(),599                const_on_chain_schema: Vec::new(),600                limits: CollectionLimits::default()601            };602603            // Add new collection to map604            <Collection<T>>::insert(next_id, new_collection);605606            // call event607            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));608609            Ok(())610        }611612        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.613        /// 614        /// # Permissions615        /// 616        /// * Collection Owner.617        /// 618        /// # Arguments619        /// 620        /// * collection_id: collection to destroy.621        #[weight = <T as Config>::WeightInfo::destroy_collection()]622        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {623624            let sender = ensure_signed(origin)?;625            Self::check_owner_permissions(collection_id, sender)?;626627            <AddressTokens<T>>::remove_prefix(collection_id);628            <Allowances<T>>::remove_prefix(collection_id);629            <Balance<T>>::remove_prefix(collection_id);630            <ItemListIndex>::remove(collection_id);631            <AdminList<T>>::remove(collection_id);632            <Collection<T>>::remove(collection_id);633            <WhiteList<T>>::remove_prefix(collection_id);634635            <NftItemList<T>>::remove_prefix(collection_id);636            <FungibleItemList<T>>::remove_prefix(collection_id);637            <ReFungibleItemList<T>>::remove_prefix(collection_id);638639            <NftTransferBasket<T>>::remove_prefix(collection_id);640            <FungibleTransferBasket<T>>::remove_prefix(collection_id);641            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);642643            if CollectionCount::get() > 0644            {645                // bound couter646                let total = CollectionCount::get()647                    .checked_sub(1)648                    .ok_or(Error::<T>::NumOverflow)?;649650                CollectionCount::put(total);651            }652653            Ok(())654        }655656        /// Add an address to white list.657        /// 658        /// # Permissions659        /// 660        /// * Collection Owner661        /// * Collection Admin662        /// 663        /// # Arguments664        /// 665        /// * collection_id.666        /// 667        /// * address.668        #[weight = <T as Config>::WeightInfo::add_to_white_list()]669        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{670671            let sender = ensure_signed(origin)?;672            Self::check_owner_or_admin_permissions(collection_id, sender)?;673674            <WhiteList<T>>::insert(collection_id, address, true);675            676            Ok(())677        }678679        /// Remove an address from white list.680        /// 681        /// # Permissions682        /// 683        /// * Collection Owner684        /// * Collection Admin685        /// 686        /// # Arguments687        /// 688        /// * collection_id.689        /// 690        /// * address.691        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]692        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{693694            let sender = ensure_signed(origin)?;695            Self::check_owner_or_admin_permissions(collection_id, sender)?;696697            <WhiteList<T>>::remove(collection_id, address);698699            Ok(())700        }701702        /// Toggle between normal and white list access for the methods with access for `Anyone`.703        /// 704        /// # Permissions705        /// 706        /// * Collection Owner.707        /// 708        /// # Arguments709        /// 710        /// * collection_id.711        /// 712        /// * mode: [AccessMode]713        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]714        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult715        {716            let sender = ensure_signed(origin)?;717718            Self::check_owner_permissions(collection_id, sender)?;719            let mut target_collection = <Collection<T>>::get(collection_id);720            target_collection.access = mode;721            <Collection<T>>::insert(collection_id, target_collection);722723            Ok(())724        }725726        /// Allows Anyone to create tokens if:727        /// * White List is enabled, and728        /// * Address is added to white list, and729        /// * This method was called with True parameter730        /// 731        /// # Permissions732        /// * Collection Owner733        ///734        /// # Arguments735        /// 736        /// * collection_id.737        /// 738        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.739        #[weight = <T as Config>::WeightInfo::set_mint_permission()]740        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult741        {742            let sender = ensure_signed(origin)?;743744            Self::check_owner_permissions(collection_id, sender)?;745            let mut target_collection = <Collection<T>>::get(collection_id);746            target_collection.mint_mode = mint_permission;747            <Collection<T>>::insert(collection_id, target_collection);748749            Ok(())750        }751752        /// Change the owner of the collection.753        /// 754        /// # Permissions755        /// 756        /// * Collection Owner.757        /// 758        /// # Arguments759        /// 760        /// * collection_id.761        /// 762        /// * new_owner.763        #[weight = <T as Config>::WeightInfo::change_collection_owner()]764        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {765766            let sender = ensure_signed(origin)?;767            Self::check_owner_permissions(collection_id, sender)?;768            let mut target_collection = <Collection<T>>::get(collection_id);769            target_collection.owner = new_owner;770            <Collection<T>>::insert(collection_id, target_collection);771772            Ok(())773        }774775        /// Adds an admin of the Collection.776        /// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership. 777        /// 778        /// # Permissions779        /// 780        /// * Collection Owner.781        /// * Collection Admin.782        /// 783        /// # Arguments784        /// 785        /// * collection_id: ID of the Collection to add admin for.786        /// 787        /// * new_admin_id: Address of new admin to add.788        #[weight = <T as Config>::WeightInfo::add_collection_admin()]789        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {790791            let sender = ensure_signed(origin)?;792            Self::check_owner_or_admin_permissions(collection_id, sender)?;793            let mut admin_arr: Vec<T::AccountId> = Vec::new();794795            if <AdminList<T>>::contains_key(collection_id)796            {797                admin_arr = <AdminList<T>>::get(collection_id);798                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);799            }800801            // Number of collection admins802            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);803804            admin_arr.push(new_admin_id);805            <AdminList<T>>::insert(collection_id, admin_arr);806807            Ok(())808        }809810        /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.811        ///812        /// # Permissions813        /// 814        /// * Collection Owner.815        /// * Collection Admin.816        /// 817        /// # Arguments818        /// 819        /// * collection_id: ID of the Collection to remove admin for.820        /// 821        /// * account_id: Address of admin to remove.822        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]823        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {824825            let sender = ensure_signed(origin)?;826            Self::check_owner_or_admin_permissions(collection_id, sender)?;827            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);828829            let mut admin_arr = <AdminList<T>>::get(collection_id);830            admin_arr.retain(|i| *i != account_id);831            <AdminList<T>>::insert(collection_id, admin_arr);832833            Ok(())834        }835836        /// # Permissions837        /// 838        /// * Collection Owner839        /// 840        /// # Arguments841        /// 842        /// * collection_id.843        /// 844        /// * new_sponsor.845        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]846        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {847848            let sender = ensure_signed(origin)?;849            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);850851            let mut target_collection = <Collection<T>>::get(collection_id);852            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);853854            target_collection.sponsor = new_sponsor;855            target_collection.sponsor_confirmed = false;856            <Collection<T>>::insert(collection_id, target_collection);857858            Ok(())859        }860861        /// # Permissions862        /// 863        /// * Sponsor.864        /// 865        /// # Arguments866        /// 867        /// * collection_id.868        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]869        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {870871            let sender = ensure_signed(origin)?;872            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);873874            let mut target_collection = <Collection<T>>::get(collection_id);875            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);876877            target_collection.sponsor_confirmed = true;878            <Collection<T>>::insert(collection_id, target_collection);879880            Ok(())881        }882883        /// Switch back to pay-per-own-transaction model.884        ///885        /// # Permissions886        ///887        /// * Collection owner.888        /// 889        /// # Arguments890        /// 891        /// * collection_id.892        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]893        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {894895            let sender = ensure_signed(origin)?;896            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);897898            let mut target_collection = <Collection<T>>::get(collection_id);899            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);900901            target_collection.sponsor = T::AccountId::default();902            target_collection.sponsor_confirmed = false;903            <Collection<T>>::insert(collection_id, target_collection);904905            Ok(())906        }907908        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.909        /// 910        /// # Permissions911        /// 912        /// * Collection Owner.913        /// * Collection Admin.914        /// * Anyone if915        ///     * White List is enabled, and916        ///     * Address is added to white list, and917        ///     * MintPermission is enabled (see SetMintPermission method)918        /// 919        /// # Arguments920        /// 921        /// * collection_id: ID of the collection.922        /// 923        /// * owner: Address, initial owner of the NFT.924        ///925        /// * data: Token data to store on chain.926        // #[weight =927        // (130_000_000 as Weight)928        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))929        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))930        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]931932        #[weight = <T as Config>::WeightInfo::create_item(data.len())]933        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {934935            let sender = ensure_signed(origin)?;936937            Self::collection_exists(collection_id)?;938939            let target_collection = <Collection<T>>::get(collection_id);940941            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;942            Self::validate_create_item_args(&target_collection, &data)?;943            Self::create_item_no_validation(collection_id, owner, data)?;944945            Ok(())946        }947948        /// This method creates multiple instances of NFT Collection created with CreateCollection method.949        /// 950        /// # Permissions951        /// 952        /// * Collection Owner.953        /// * Collection Admin.954        /// * Anyone if955        ///     * White List is enabled, and956        ///     * Address is added to white list, and957        ///     * MintPermission is enabled (see SetMintPermission method)958        /// 959        /// # Arguments960        /// 961        /// * collection_id: ID of the collection.962        /// 963        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].964        /// 965        /// * owner: Address, initial owner of the NFT.966        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()967                               .map(|data| { data.len() })968                               .sum())]969        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {970971            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);972            let sender = ensure_signed(origin)?;973974            Self::collection_exists(collection_id)?;975            let target_collection = <Collection<T>>::get(collection_id);976977            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;978979            for data in &items_data {980                Self::validate_create_item_args(&target_collection, data)?;981            }982            for data in &items_data {983                Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?;984            }985986            Ok(())987        }988989        /// Destroys a concrete instance of NFT.990        /// 991        /// # Permissions992        /// 993        /// * Collection Owner.994        /// * Collection Admin.995        /// * Current NFT Owner.996        /// 997        /// # Arguments998        /// 999        /// * collection_id: ID of the collection.1000        /// 1001        /// * item_id: ID of NFT to burn.1002        #[weight = <T as Config>::WeightInfo::burn_item()]1003        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10041005            let sender = ensure_signed(origin)?;1006            Self::collection_exists(collection_id)?;10071008            // Transfer permissions check1009            let target_collection = <Collection<T>>::get(collection_id);1010            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1011                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1012                Error::<T>::NoPermission);10131014            if target_collection.access == AccessMode::WhiteList {1015                Self::check_white_list(collection_id, &sender)?;1016            }10171018            match target_collection.mode1019            {1020                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1021                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1022                CollectionMode::ReFungible  => Self::burn_refungible_item(collection_id, item_id, &sender)?,1023                _ => ()1024            };10251026            // call event1027            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10281029            Ok(())1030        }10311032        /// Change ownership of the token.1033        /// 1034        /// # Permissions1035        /// 1036        /// * Collection Owner1037        /// * Collection Admin1038        /// * Current NFT owner1039        ///1040        /// # Arguments1041        /// 1042        /// * recipient: Address of token recipient.1043        /// 1044        /// * collection_id.1045        /// 1046        /// * item_id: ID of the item1047        ///     * Non-Fungible Mode: Required.1048        ///     * Fungible Mode: Ignored.1049        ///     * Re-Fungible Mode: Required.1050        /// 1051        /// * value: Amount to transfer.1052        ///     * Non-Fungible Mode: Ignored1053        ///     * Fungible Mode: Must specify transferred amount1054        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1055        #[weight = <T as Config>::WeightInfo::transfer()]1056        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1057            let sender = ensure_signed(origin)?;1058            Self::transfer_internal(sender, recipient, collection_id, item_id, value)1059        }10601061        /// Set, change, or remove approved address to transfer the ownership of the NFT.1062        /// 1063        /// # Permissions1064        /// 1065        /// * Collection Owner1066        /// * Collection Admin1067        /// * Current NFT owner1068        /// 1069        /// # Arguments1070        /// 1071        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1072        /// 1073        /// * collection_id.1074        /// 1075        /// * item_id: ID of the item.1076        #[weight = <T as Config>::WeightInfo::approve()]1077        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {10781079            let sender = ensure_signed(origin)?;10801081            Self::collection_exists(collection_id)?;1082            Self::token_exists(collection_id, item_id, &sender)?;10831084            // Transfer permissions check1085            let target_collection = <Collection<T>>::get(collection_id);1086            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1087                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1088                Error::<T>::NoPermission);10891090            if target_collection.access == AccessMode::WhiteList {1091                Self::check_white_list(collection_id, &sender)?;1092                Self::check_white_list(collection_id, &spender)?;1093            }10941095            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1096            let mut allowance: u128 = amount;1097            if allowance_exists {1098                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1099            }1100            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11011102            Ok(())1103        }1104        1105        /// 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.1106        /// 1107        /// # Permissions1108        /// * Collection Owner1109        /// * Collection Admin1110        /// * Current NFT owner1111        /// * Address approved by current NFT owner1112        /// 1113        /// # Arguments1114        /// 1115        /// * from: Address that owns token.1116        /// 1117        /// * recipient: Address of token recipient.1118        /// 1119        /// * collection_id.1120        /// 1121        /// * item_id: ID of the item.1122        /// 1123        /// * value: Amount to transfer.1124        #[weight = <T as Config>::WeightInfo::transfer_from()]1125        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11261127            let sender = ensure_signed(origin)?;1128            let mut appoved_transfer = false;11291130            // Check approval1131            let mut approval: u128 = 0;1132            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1133                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1134                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1135                appoved_transfer = true;1136            }11371138            let target_collection = <Collection<T>>::get(collection_id);11391140            // Limits check1141            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11421143            // Transfer permissions check         1144            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1145                Error::<T>::NoPermission);11461147            if target_collection.access == AccessMode::WhiteList {1148                Self::check_white_list(collection_id, &sender)?;1149                Self::check_white_list(collection_id, &recipient)?;1150            }11511152            // Reduce approval by transferred amount or remove if remaining approval drops to 01153            if approval.checked_sub(value).unwrap_or(0) > 0 {1154                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1155            }1156            else {1157                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1158            }11591160            match target_collection.mode1161            {1162                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1163                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1164                CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1165                _ => ()1166            };11671168            Ok(())1169        }11701171        // #[weight = 0]1172        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11731174        //     // let no_perm_mes = "You do not have permissions to modify this collection";1175        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1176        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1177        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11781179        //     // // on_nft_received  call11801181        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;11821183        //     Ok(())1184        // }11851186        /// Set off-chain data schema.1187        /// 1188        /// # Permissions1189        /// 1190        /// * Collection Owner1191        /// * Collection Admin1192        /// 1193        /// # Arguments1194        /// 1195        /// * collection_id.1196        /// 1197        /// * schema: String representing the offchain data schema.1198        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1199        pub fn set_variable_meta_data (1200            origin,1201            collection_id: CollectionId,1202            item_id: TokenId,1203            data: Vec<u8>1204        ) -> DispatchResult {1205            let sender = ensure_signed(origin)?;1206            1207            Self::collection_exists(collection_id)?;1208            Self::token_exists(collection_id, item_id, &sender)?;12091210            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12111212            // Modify permissions check1213            let target_collection = <Collection<T>>::get(collection_id);1214            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1215                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1216                Error::<T>::NoPermission);12171218            match target_collection.mode1219            {1220                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1221                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1222                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1223                _ => fail!(Error::<T>::UnexpectedCollectionType)1224            };12251226            Ok(())1227        }1228 1229        /// Set schema standard1230        /// ImageURL1231        /// Unique1232        /// 1233        /// # Permissions1234        /// 1235        /// * Collection Owner1236        /// * Collection Admin1237        /// 1238        /// # Arguments1239        /// 1240        /// * collection_id.1241        /// 1242        /// * schema: SchemaVersion: enum1243        #[weight = <T as Config>::WeightInfo::set_schema_version()]1244        pub fn set_schema_version(1245            origin,1246            collection_id: CollectionId,1247            version: SchemaVersion1248        ) -> DispatchResult {1249            let sender = ensure_signed(origin)?;1250            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1251            let mut target_collection = <Collection<T>>::get(collection_id);1252            target_collection.schema_version = version;1253            <Collection<T>>::insert(collection_id, target_collection);12541255            Ok(())1256        }12571258        /// Set off-chain data schema.1259        /// 1260        /// # Permissions1261        /// 1262        /// * Collection Owner1263        /// * Collection Admin1264        /// 1265        /// # Arguments1266        /// 1267        /// * collection_id.1268        /// 1269        /// * schema: String representing the offchain data schema.1270        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1271        pub fn set_offchain_schema(1272            origin,1273            collection_id: CollectionId,1274            schema: Vec<u8>1275        ) -> DispatchResult {1276            let sender = ensure_signed(origin)?;1277            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12781279            // check schema limit1280            ensure!(schema.len() as u32 > ChainLimit::get().offchain_schema_limit, "");12811282            let mut target_collection = <Collection<T>>::get(collection_id);1283            target_collection.offchain_schema = schema;1284            <Collection<T>>::insert(collection_id, target_collection);12851286            Ok(())1287        }12881289        /// Set const on-chain data schema.1290        /// 1291        /// # Permissions1292        /// 1293        /// * Collection Owner1294        /// * Collection Admin1295        /// 1296        /// # Arguments1297        /// 1298        /// * collection_id.1299        /// 1300        /// * schema: String representing the const on-chain data schema.1301        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1302        pub fn set_const_on_chain_schema (1303            origin,1304            collection_id: CollectionId,1305            schema: Vec<u8>1306        ) -> DispatchResult {1307            let sender = ensure_signed(origin)?;1308            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13091310            // check schema limit1311            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");13121313            let mut target_collection = <Collection<T>>::get(collection_id);1314            target_collection.const_on_chain_schema = schema;1315            <Collection<T>>::insert(collection_id, target_collection);13161317            Ok(())1318        }13191320        /// Set variable on-chain data schema.1321        /// 1322        /// # Permissions1323        /// 1324        /// * Collection Owner1325        /// * Collection Admin1326        /// 1327        /// # Arguments1328        /// 1329        /// * collection_id.1330        /// 1331        /// * schema: String representing the variable on-chain data schema.1332        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1333        pub fn set_variable_on_chain_schema (1334            origin,1335            collection_id: CollectionId,1336            schema: Vec<u8>1337        ) -> DispatchResult {1338            let sender = ensure_signed(origin)?;1339            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13401341            // check schema limit1342            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");13431344            let mut target_collection = <Collection<T>>::get(collection_id);1345            target_collection.variable_on_chain_schema = schema;1346            <Collection<T>>::insert(collection_id, target_collection);13471348            Ok(())1349        }13501351        // Sudo permissions function1352        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1353        pub fn set_chain_limits(1354            origin,1355            limits: ChainLimits1356        ) -> DispatchResult {13571358            #[cfg(not(feature = "runtime-benchmarks"))]1359            ensure_root(origin)?;13601361            <ChainLimit>::put(limits);1362            Ok(())1363        }13641365        /// Enable smart contract self-sponsoring.1366        /// 1367        /// # Permissions1368        /// 1369        /// * Contract Owner1370        /// 1371        /// # Arguments1372        /// 1373        /// * contract address1374        /// * enable flag1375        /// 1376        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1377        pub fn enable_contract_sponsoring(1378            origin,1379            contract_address: T::AccountId,1380            enable: bool1381        ) -> DispatchResult {13821383            let sender = ensure_signed(origin)?;13841385            #[cfg(feature = "runtime-benchmarks")]1386            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13871388            Self::ensure_contract_owned(sender, &contract_address)?;13891390            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1391            Ok(())1392        }13931394        /// Set the rate limit for contract sponsoring to specified number of blocks.1395        /// 1396        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1397        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1398        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1399        /// from contract endowment if there are at least B blocks between such transactions. 1400        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1401        /// 1402        /// # Permissions1403        /// 1404        /// * Contract Owner1405        /// 1406        /// # Arguments1407        /// 1408        /// -`contract_address`: Address of the contract to sponsor1409        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1410        /// 1411        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1412        pub fn set_contract_sponsoring_rate_limit(1413            origin,1414            contract_address: T::AccountId,1415            rate_limit: T::BlockNumber1416        ) -> DispatchResult {1417            let sender = ensure_signed(origin)?;14181419            #[cfg(feature = "runtime-benchmarks")]1420            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14211422            Self::ensure_contract_owned(sender, &contract_address)?;1423            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1424            Ok(())1425        }14261427        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1428        /// 1429        /// # Permissions1430        /// 1431        /// * Address that deployed smart contract.1432        /// 1433        /// # Arguments1434        /// 1435        /// -`contract_address`: Address of the contract.1436        /// 1437        /// - `enable`: .  1438        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1439        pub fn toggle_contract_white_list(1440            origin,1441            contract_address: T::AccountId,1442            enable: bool1443        ) -> DispatchResult {1444            let sender = ensure_signed(origin)?;14451446            #[cfg(feature = "runtime-benchmarks")]1447            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14481449            Self::ensure_contract_owned(sender, &contract_address)?;1450            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1451            Ok(())1452        }1453        1454        /// Add an address to smart contract white list.1455        /// 1456        /// # Permissions1457        /// 1458        /// * Address that deployed smart contract.1459        /// 1460        /// # Arguments1461        /// 1462        /// -`contract_address`: Address of the contract.1463        ///1464        /// -`account_address`: Address to add.1465        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1466        pub fn add_to_contract_white_list(1467            origin,1468            contract_address: T::AccountId,1469            account_address: T::AccountId1470        ) -> DispatchResult {1471            let sender = ensure_signed(origin)?;14721473            #[cfg(feature = "runtime-benchmarks")]1474            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1475            1476            Self::ensure_contract_owned(sender, &contract_address)?;      1477            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1478            Ok(())1479        }14801481        /// Remove an address from smart contract white list.1482        /// 1483        /// # Permissions1484        /// 1485        /// * Address that deployed smart contract.1486        /// 1487        /// # Arguments1488        /// 1489        /// -`contract_address`: Address of the contract.1490        ///1491        /// -`account_address`: Address to remove.1492        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1493        pub fn remove_from_contract_white_list(1494            origin,1495            contract_address: T::AccountId,1496            account_address: T::AccountId1497        ) -> DispatchResult {1498            let sender = ensure_signed(origin)?;14991500            #[cfg(feature = "runtime-benchmarks")]1501            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15021503            Self::ensure_contract_owned(sender, &contract_address)?;1504            <ContractWhiteList<T>>::remove(contract_address, account_address);1505            Ok(())1506        }15071508        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1509        pub fn set_collection_limits(1510            origin,1511            collection_id: u32,1512            limits: CollectionLimits,1513        ) -> DispatchResult {1514            let sender = ensure_signed(origin)?;1515            Self::check_owner_permissions(collection_id, sender.clone())?;1516            let mut target_collection = <Collection<T>>::get(collection_id);1517            let chain_limits = ChainLimit::get();1518            let climits = target_collection.limits;15191520            // collection bounds1521            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1522                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1523                Error::<T>::CollectionLimitBoundsExceeded);15241525            // token_limit   check  prev1526            ensure!(climits.token_limit > limits.token_limit && 1527                limits.token_limit <= chain_limits.account_token_ownership_limit, 1528                Error::<T>::AccountTokenLimitExceeded);15291530            target_collection.limits = limits;1531            <Collection<T>>::insert(collection_id, target_collection);15321533            Ok(())1534        } 1535    }1536}15371538impl<T: Config> Module<T> {15391540    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {15411542        let target_collection = <Collection<T>>::get(collection_id);15431544        // Limits check1545        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;15461547        // Transfer permissions check1548        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1549            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1550            Error::<T>::NoPermission);15511552        if target_collection.access == AccessMode::WhiteList {1553            Self::check_white_list(collection_id, &sender)?;1554            Self::check_white_list(collection_id, &recipient)?;1555        }15561557        match target_collection.mode1558        {1559            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1560            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1561            CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1562            _ => ()1563        };15641565        Ok(())1566    }156715681569    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15701571        // check token limit and account token limit1572        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1573        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1574        1575        Ok(())1576    }15771578    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15791580        // check token limit and account token limit1581        let total_items: u32 = ItemListIndex::get(collection_id);1582        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1583        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1584        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15851586        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1587            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1588            Self::check_white_list(collection_id, owner)?;1589            Self::check_white_list(collection_id, sender)?;1590        }15911592        Ok(())1593    }15941595    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1596        match target_collection.mode1597        {1598            CollectionMode::NFT => {1599                if let CreateItemData::NFT(data) = data {1600                    // check sizes1601                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1602                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1603                } else {1604                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1605                }1606            },1607            CollectionMode::Fungible(_) => {1608                if let CreateItemData::Fungible(_) = data {1609                } else {1610                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1611                }1612            },1613            CollectionMode::ReFungible => {1614                if let CreateItemData::ReFungible(data) = data {16151616                    // check sizes1617                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1618                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16191620                    // Check refungibility limits1621                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1622                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1623                } else {1624                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1625                }1626            },1627            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1628        };16291630        Ok(())1631    }16321633    fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1634        match data1635        {1636            CreateItemData::NFT(data) => {1637                let item = NftItemType {1638                    owner,1639                    const_data: data.const_data,1640                    variable_data: data.variable_data1641                };16421643                Self::add_nft_item(collection_id, item)?;1644            },1645            CreateItemData::Fungible(data) => {1646                Self::add_fungible_item(collection_id, &owner, data.value)?;1647            },1648            CreateItemData::ReFungible(data) => {1649                let mut owner_list = Vec::new();1650                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});16511652                let item = ReFungibleItemType {1653                    owner: owner_list,1654                    const_data: data.const_data,1655                    variable_data: data.variable_data1656                };16571658                Self::add_refungible_item(collection_id, item)?;1659            }1660        };16611662        // call event1663        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16641665        Ok(())1666    }16671668    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {16691670        // Does new owner already have an account?1671        let mut balance: u128 = 0;1672        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1673            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1674        } 16751676        // Mint 1677        let item = FungibleItemType {1678            value: balance + value1679        };1680        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16811682        // Update balance1683        let new_balance = <Balance<T>>::get(collection_id, owner)1684            .checked_add(value)1685            .ok_or(Error::<T>::NumOverflow)?;1686        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);16871688        Ok(())1689    }16901691    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1692        let current_index = <ItemListIndex>::get(collection_id)1693            .checked_add(1)1694            .ok_or(Error::<T>::NumOverflow)?;1695        let itemcopy = item.clone();16961697        let value = item.owner.first().unwrap().fraction;1698        let owner = item.owner.first().unwrap().owner.clone();16991700        Self::add_token_index(collection_id, current_index, &owner)?;17011702        <ItemListIndex>::insert(collection_id, current_index);1703        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17041705        // Update balance1706        let new_balance = <Balance<T>>::get(collection_id, &owner)1707            .checked_add(value)1708            .ok_or(Error::<T>::NumOverflow)?;1709        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17101711        Ok(())1712    }17131714    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1715        let current_index = <ItemListIndex>::get(collection_id)1716            .checked_add(1)1717            .ok_or(Error::<T>::NumOverflow)?;17181719        let item_owner = item.owner.clone();1720        Self::add_token_index(collection_id, current_index, &item.owner)?;17211722        <ItemListIndex>::insert(collection_id, current_index);1723        <NftItemList<T>>::insert(collection_id, current_index, item);17241725        // Update balance1726        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1727            .checked_add(1)1728            .ok_or(Error::<T>::NumOverflow)?;1729        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17301731        Ok(())1732    }17331734    fn burn_refungible_item(1735        collection_id: CollectionId,1736        item_id: TokenId,1737        owner: &T::AccountId,1738    ) -> DispatchResult {1739        ensure!(1740            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1741            Error::<T>::TokenNotFound1742        );1743        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1744        let rft_balance = token1745            .owner1746            .iter()1747            .filter(|&i| i.owner == *owner)1748            .next()1749            .unwrap();1750        Self::remove_token_index(collection_id, item_id, owner)?;17511752        // update balance1753        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1754            .checked_sub(rft_balance.fraction)1755            .ok_or(Error::<T>::NumOverflow)?;1756        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);17571758        // Re-create owners list with sender removed1759        let index = token1760            .owner1761            .iter()1762            .position(|i| i.owner == *owner)1763            .unwrap();1764        token.owner.remove(index);1765        let owner_count = token.owner.len();17661767        // Burn the token completely if this was the last (only) owner1768        if owner_count == 0 {1769            <ReFungibleItemList<T>>::remove(collection_id, item_id);1770        }1771        else {1772            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1773        }17741775        Ok(())1776    }17771778    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1779        ensure!(1780            <NftItemList<T>>::contains_key(collection_id, item_id),1781            Error::<T>::TokenNotFound1782        );1783        let item = <NftItemList<T>>::get(collection_id, item_id);1784        Self::remove_token_index(collection_id, item_id, &item.owner)?;17851786        // update balance1787        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1788            .checked_sub(1)1789            .ok_or(Error::<T>::NumOverflow)?;1790        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1791        <NftItemList<T>>::remove(collection_id, item_id);17921793        Ok(())1794    }17951796    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1797        ensure!(1798            <FungibleItemList<T>>::contains_key(collection_id, owner),1799            Error::<T>::TokenNotFound1800        );1801        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1802        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18031804        // update balance1805        let new_balance = <Balance<T>>::get(collection_id, owner)1806            .checked_sub(value)1807            .ok_or(Error::<T>::NumOverflow)?;1808        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18091810        if balance.value - value > 0 {1811            balance.value -= value;1812            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1813        }1814        else {1815            <FungibleItemList<T>>::remove(collection_id, owner);1816        }18171818        Ok(())1819    }18201821    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1822        ensure!(1823            <Collection<T>>::contains_key(collection_id),1824            Error::<T>::CollectionNotFound1825        );1826        Ok(())1827    }18281829    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1830        Self::collection_exists(collection_id)?;18311832        let target_collection = <Collection<T>>::get(collection_id);1833        ensure!(1834            subject == target_collection.owner,1835            Error::<T>::NoPermission1836        );18371838        Ok(())1839    }18401841    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1842        let target_collection = <Collection<T>>::get(collection_id);1843        let mut result: bool = subject == target_collection.owner;1844        let exists = <AdminList<T>>::contains_key(collection_id);18451846        if !result & exists {1847            if <AdminList<T>>::get(collection_id).contains(&subject) {1848                result = true1849            }1850        }18511852        result1853    }18541855    fn check_owner_or_admin_permissions(1856        collection_id: CollectionId,1857        subject: T::AccountId,1858    ) -> DispatchResult {1859        Self::collection_exists(collection_id)?;1860        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18611862        ensure!(1863            result,1864            Error::<T>::NoPermission1865        );1866        Ok(())1867    }18681869    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1870        let target_collection = <Collection<T>>::get(collection_id);18711872        match target_collection.mode {1873            CollectionMode::NFT => {1874                <NftItemList<T>>::get(collection_id, item_id).owner == subject1875            }1876            CollectionMode::Fungible(_) => {1877                <FungibleItemList<T>>::contains_key(collection_id, &subject)1878            }1879            CollectionMode::ReFungible => {1880                <ReFungibleItemList<T>>::get(collection_id, item_id)1881                    .owner1882                    .iter()1883                    .any(|i| i.owner == subject)1884            }1885            CollectionMode::Invalid => false,1886        }1887    }18881889    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1890        let mes = Error::<T>::AddresNotInWhiteList;1891        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);18921893        Ok(())1894    }18951896    /// Check if token exists. In case of Fungible, check if there is an entry for 1897    /// the owner in fungible balances double map1898    fn token_exists(1899        collection_id: CollectionId,1900        item_id: TokenId,1901        owner: &T::AccountId1902    ) -> DispatchResult {1903        let target_collection = <Collection<T>>::get(collection_id);1904        let exists = match target_collection.mode1905        {1906            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1907            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),1908            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1909            _ => false1910        };19111912        ensure!(exists == true, Error::<T>::TokenNotFound);1913        Ok(())1914    }19151916    fn transfer_fungible(1917        collection_id: CollectionId,1918        value: u128,1919        owner: &T::AccountId,1920        recipient: &T::AccountId,1921    ) -> DispatchResult {1922        Self::token_exists(collection_id, 0, owner)?;19231924        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1925        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19261927        // Send balance to recipient (updates balanceOf of recipient)1928        Self::add_fungible_item(collection_id, recipient, value)?;19291930        // update balanceOf of sender1931        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);19321933        // Reduce or remove sender1934        if balance.value == value {1935            <FungibleItemList<T>>::remove(collection_id, owner);1936        }1937        else {1938            balance.value -= value;1939            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1940        }19411942        Ok(())1943    }19441945    fn transfer_refungible(1946        collection_id: CollectionId,1947        item_id: TokenId,1948        value: u128,1949        owner: T::AccountId,1950        new_owner: T::AccountId,1951    ) -> DispatchResult {1952        Self::token_exists(collection_id, item_id, &owner)?;19531954        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1955        let item = full_item1956            .owner1957            .iter()1958            .filter(|i| i.owner == owner)1959            .next()1960            .ok_or(Error::<T>::NumOverflow)?;1961        let amount = item.fraction;19621963        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19641965        // update balance1966        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1967            .checked_sub(value)1968            .ok_or(Error::<T>::NumOverflow)?;1969        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19701971        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1972            .checked_add(value)1973            .ok_or(Error::<T>::NumOverflow)?;1974        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19751976        let old_owner = item.owner.clone();1977        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19781979        // transfer1980        if amount == value && !new_owner_has_account {1981            // change owner1982            // new owner do not have account1983            let mut new_full_item = full_item.clone();1984            new_full_item1985                .owner1986                .iter_mut()1987                .find(|i| i.owner == owner)1988                .unwrap()1989                .owner = new_owner.clone();1990            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19911992            // update index collection1993            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;1994        } else {1995            let mut new_full_item = full_item.clone();1996            new_full_item1997                .owner1998                .iter_mut()1999                .find(|i| i.owner == owner)2000                .unwrap()2001                .fraction -= value;20022003            // separate amount2004            if new_owner_has_account {2005                // new owner has account2006                new_full_item2007                    .owner2008                    .iter_mut()2009                    .find(|i| i.owner == new_owner)2010                    .unwrap()2011                    .fraction += value;2012            } else {2013                // new owner do not have account2014                new_full_item.owner.push(Ownership {2015                    owner: new_owner.clone(),2016                    fraction: value,2017                });2018                Self::add_token_index(collection_id, item_id, &new_owner)?;2019            }20202021            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2022        }20232024        Ok(())2025    }20262027    fn transfer_nft(2028        collection_id: CollectionId,2029        item_id: TokenId,2030        sender: T::AccountId,2031        new_owner: T::AccountId,2032    ) -> DispatchResult {2033        Self::token_exists(collection_id, item_id, &sender)?;20342035        let mut item = <NftItemList<T>>::get(collection_id, item_id);20362037        ensure!(2038            sender == item.owner,2039            Error::<T>::MustBeTokenOwner2040        );20412042        // update balance2043        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2044            .checked_sub(1)2045            .ok_or(Error::<T>::NumOverflow)?;2046        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20472048        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2049            .checked_add(1)2050            .ok_or(Error::<T>::NumOverflow)?;2051        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20522053        // change owner2054        let old_owner = item.owner.clone();2055        item.owner = new_owner.clone();2056        <NftItemList<T>>::insert(collection_id, item_id, item);20572058        // update index collection2059        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;20602061        Ok(())2062    }2063    2064    fn set_re_fungible_variable_data(2065        collection_id: CollectionId,2066        item_id: TokenId,2067        data: Vec<u8>2068    ) -> DispatchResult {2069        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20702071        item.variable_data = data;20722073        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20742075        Ok(())2076    }20772078    fn set_nft_variable_data(2079        collection_id: CollectionId,2080        item_id: TokenId,2081        data: Vec<u8>2082    ) -> DispatchResult {2083        let mut item = <NftItemList<T>>::get(collection_id, item_id);2084        2085        item.variable_data = data;20862087        <NftItemList<T>>::insert(collection_id, item_id, item);2088        2089        Ok(())2090    }20912092    fn init_collection(item: &CollectionType<T::AccountId>) {2093        // check params2094        assert!(2095            item.decimal_points <= MAX_DECIMAL_POINTS,2096            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2097        );2098        assert!(2099            item.name.len() <= 64,2100            "Collection name can not be longer than 63 char"2101        );2102        assert!(2103            item.name.len() <= 256,2104            "Collection description can not be longer than 255 char"2105        );2106        assert!(2107            item.token_prefix.len() <= 16,2108            "Token prefix can not be longer than 15 char"2109        );21102111        // Generate next collection ID2112        let next_id = CreatedCollectionCount::get()2113            .checked_add(1)2114            .unwrap();21152116        CreatedCollectionCount::put(next_id);2117    }21182119    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2120        let current_index = <ItemListIndex>::get(collection_id)2121            .checked_add(1)2122            .unwrap();21232124        let item_owner = item.owner.clone();2125        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21262127        <ItemListIndex>::insert(collection_id, current_index);21282129        // Update balance2130        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2131            .checked_add(1)2132            .unwrap();2133        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2134    }21352136    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2137        let current_index = <ItemListIndex>::get(collection_id)2138            .checked_add(1)2139            .unwrap();21402141        Self::add_token_index(collection_id, current_index, owner).unwrap();21422143        <ItemListIndex>::insert(collection_id, current_index);21442145        // Update balance2146        let new_balance = <Balance<T>>::get(collection_id, owner)2147            .checked_add(item.value)2148            .unwrap();2149        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2150    }21512152    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2153        let current_index = <ItemListIndex>::get(collection_id)2154            .checked_add(1)2155            .unwrap();21562157        let value = item.owner.first().unwrap().fraction;2158        let owner = item.owner.first().unwrap().owner.clone();21592160        Self::add_token_index(collection_id, current_index, &owner).unwrap();21612162        <ItemListIndex>::insert(collection_id, current_index);21632164        // Update balance2165        let new_balance = <Balance<T>>::get(collection_id, &owner)2166            .checked_add(value)2167            .unwrap();2168        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2169    }21702171    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {21722173        // add to account limit2174        if <AccountItemCount<T>>::contains_key(owner) {21752176            // bound Owned tokens by a single address2177            let count = <AccountItemCount<T>>::get(owner);2178            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21792180            <AccountItemCount<T>>::insert(owner.clone(), count2181                .checked_add(1)2182                .ok_or(Error::<T>::NumOverflow)?);2183        }2184        else {2185            <AccountItemCount<T>>::insert(owner.clone(), 1);2186        }21872188        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2189        if list_exists {2190            let mut list = <AddressTokens<T>>::get(collection_id, owner);2191            let item_contains = list.contains(&item_index.clone());21922193            if !item_contains {2194                list.push(item_index.clone());2195            }21962197            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2198        } else {2199            let mut itm = Vec::new();2200            itm.push(item_index.clone());2201            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2202        }22032204        Ok(())2205    }22062207    fn remove_token_index(2208        collection_id: CollectionId,2209        item_index: TokenId,2210        owner: &T::AccountId,2211    ) -> DispatchResult {22122213        // update counter2214        <AccountItemCount<T>>::insert(owner.clone(), 2215            <AccountItemCount<T>>::get(owner)2216            .checked_sub(1)2217            .ok_or(Error::<T>::NumOverflow)?);221822192220        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2221        if list_exists {2222            let mut list = <AddressTokens<T>>::get(collection_id, owner);2223            let item_contains = list.contains(&item_index.clone());22242225            if item_contains {2226                list.retain(|&item| item != item_index);2227                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2228            }2229        }22302231        Ok(())2232    }22332234    fn move_token_index(2235        collection_id: CollectionId,2236        item_index: TokenId,2237        old_owner: &T::AccountId,2238        new_owner: &T::AccountId,2239    ) -> DispatchResult {2240        Self::remove_token_index(collection_id, item_index, old_owner)?;2241        Self::add_token_index(collection_id, item_index, new_owner)?;22422243        Ok(())2244    }2245    2246    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2247        if <ContractOwner<T>>::contains_key(contract.clone()) {2248            let owner = <ContractOwner<T>>::get(contract);2249            ensure!(account == owner, Error::<T>::NoPermission);2250        } else {2251            fail!(Error::<T>::NoPermission);2252        }22532254        Ok(())2255    }2256}22572258////////////////////////////////////////////////////////////////////////////////////////////////////2259// Economic models2260// #region22612262/// Fee multiplier.2263pub type Multiplier = FixedU128;22642265type BalanceOf<T> = <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::Balance;22662267/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2268/// in the queue.2269#[derive(Encode, Decode, Clone, Eq, PartialEq)]2270pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);22712272impl<T: Config + Send + Sync> sp_std::fmt::Debug 2273    for ChargeTransactionPayment<T>2274{2275	#[cfg(feature = "std")]2276	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2277		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2278	}2279	#[cfg(not(feature = "std"))]2280	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2281		Ok(())2282	}2283}22842285impl<T: Config> ChargeTransactionPayment<T>2286where2287    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2288    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2289    T::AccountId: AsRef<[u8]>,2290    T::AccountId: UncheckedFrom<T::Hash>,2291{2292    fn traditional_fee(2293        len: usize,2294        info: &DispatchInfoOf<T::Call>,2295        tip: BalanceOf<T>,2296    ) -> BalanceOf<T>2297    where2298        T::Call: Dispatchable<Info = DispatchInfo>,2299    {2300        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2301    }23022303	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2304        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2305        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2306        let len_saturation = max_block_length as u64 / (len as u64).max(1);2307        let coefficient: BalanceOf<T> = weight_saturation2308            .min(len_saturation)2309            .saturated_into::<BalanceOf<T>>();2310        final_fee2311            .saturating_mul(coefficient)2312            .saturated_into::<TransactionPriority>()2313    }23142315    fn withdraw_fee(2316        &self,2317        who: &T::AccountId,2318        call: &T::Call,2319        info: &DispatchInfoOf<T::Call>,2320        len: usize,2321	) -> Result<2322		(2323			BalanceOf<T>,2324			<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2325		),2326		TransactionValidityError,2327	> {2328        let tip = self.0;23292330        // Set fee based on call type. Creating collection costs 1 Unique.2331        // All other transactions have traditional fees so far2332        // let fee = match call.is_sub_type() {2333        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2334        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2335        //                                                 // _ => <BalanceOf<T>>::from(100)2336        // };2337        let fee = Self::traditional_fee(len, info, tip);23382339        // Only mess with balances if fee is not zero.2340        if fee.is_zero() {2341            return <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2342			.map(|i| (fee, i));2343        }23442345        // Determine who is paying transaction fee based on ecnomic model2346        // Parse call to extract collection ID and access collection sponsor2347        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2348            Some(Call::create_item(collection_id, _owner, _properties)) => {23492350                // sponsor timeout2351                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;23522353                let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;2354                let mut sponsored = true;2355                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2356                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2357                    let limit_time = last_tx_block + limit.into();2358                    if block_number <= limit_time {2359                        sponsored = false;2360                    }2361                }2362                if sponsored {2363                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2364                }23652366                // check free create limit2367                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2368                   (<Collection<T>>::get(collection_id).sponsor_confirmed) &&2369                   (sponsored)2370                {2371                    <Collection<T>>::get(collection_id).sponsor2372                } else {2373                    T::AccountId::default()2374                }2375            }2376            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2377                2378                let mut sponsor_transfer = false;2379                if <Collection<T>>::get(collection_id).sponsor_confirmed {23802381                    let collection_limits = <Collection<T>>::get(collection_id).limits;2382                    let collection_mode = <Collection<T>>::get(collection_id).mode;2383    2384                    // sponsor timeout2385                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2386                    sponsor_transfer = match collection_mode {2387                        CollectionMode::NFT => {2388    2389                            // get correct limit2390                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2391                                collection_limits.sponsor_transfer_timeout2392                            } else {2393                                ChainLimit::get().nft_sponsor_transfer_timeout2394                            };2395    2396                            let mut sponsored = true;2397                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2398                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2399                                let limit_time = last_tx_block + limit.into();2400                                if block_number <= limit_time {2401                                    sponsored = false;2402                                }2403                            }2404                            if sponsored {2405                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2406                            }24072408                            sponsored2409                        }2410                        CollectionMode::Fungible(_) => {2411    2412                            // get correct limit2413                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2414                                collection_limits.sponsor_transfer_timeout2415                            } else {2416                                ChainLimit::get().fungible_sponsor_transfer_timeout2417                            };2418    2419                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2420                            let mut sponsored = true;2421                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2422                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2423                                let limit_time = last_tx_block + limit.into();2424                                if block_number <= limit_time {2425                                    sponsored = false;2426                                }2427                            }2428                            if sponsored {2429                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2430                            }24312432                            sponsored2433                        }2434                        CollectionMode::ReFungible => {2435    2436                            // get correct limit2437                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2438                                collection_limits.sponsor_transfer_timeout2439                            } else {2440                                ChainLimit::get().refungible_sponsor_transfer_timeout2441                            };2442    2443                            let mut sponsored = true;2444                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2445                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2446                                let limit_time = last_tx_block + limit.into();2447                                if block_number <= limit_time {2448                                    sponsored = false;2449                                }2450                            }2451                            if sponsored {2452                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2453                            }24542455                            sponsored2456                        }2457                        _ => {2458                            false2459                        },2460                    };2461                }24622463                if !sponsor_transfer {2464                    T::AccountId::default()2465                } else {2466                    <Collection<T>>::get(collection_id).sponsor2467                }2468            }24692470            _ => T::AccountId::default(),2471        };24722473        // Sponsor smart contracts2474        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {24752476            // On instantiation: set the contract owner2477            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {24782479                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2480                    &who,2481                    code_hash,2482                    salt,2483                );2484                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());24852486                T::AccountId::default()2487            },24882489            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2490            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {24912492                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());24932494                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2495                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2496                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2497                  2498                if !owned_contract && white_list_enabled {2499                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2500                        return Err(InvalidTransaction::Call.into());2501                    }2502                }25032504                let mut sponsor_transfer = false;2505                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2506                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2507                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2508                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2509                    let limit_time = last_tx_block + rate_limit;25102511                    if block_number >= limit_time {2512                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2513                        sponsor_transfer = true;2514                    }2515                } else {2516                    sponsor_transfer = false;2517                }2518               2519                2520                let mut sp = T::AccountId::default();2521                if sponsor_transfer {2522                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2523                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2524                            sp = called_contract;2525                        }2526                    }2527                }25282529                sp2530            },25312532            _ => sponsor,2533        };25342535        let mut who_pays_fee: T::AccountId = sponsor.clone();2536        if sponsor == T::AccountId::default() {2537            who_pays_fee = who.clone();2538        }25392540		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2541			.map(|i| (fee, i))2542    }2543}254425452546impl<T: Config + Send + Sync> SignedExtension2547    for ChargeTransactionPayment<T>2548where2549    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2550    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2551    T::AccountId: AsRef<[u8]>,2552    T::AccountId: UncheckedFrom<T::Hash>,2553{2554    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2555    type AccountId = T::AccountId;2556    type Call = T::Call;2557    type AdditionalSigned = ();2558    type Pre = (2559        // tip2560        BalanceOf<T>,2561        // who pays fee2562        Self::AccountId,2563		// imbalance resulting from withdrawing the fee2564		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2565    );2566    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2567        Ok(())2568    }25692570    fn validate(2571        &self,2572        who: &Self::AccountId,2573        call: &Self::Call,2574        info: &DispatchInfoOf<Self::Call>,2575        len: usize,2576    ) -> TransactionValidity {2577		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2578		Ok(ValidTransaction {2579			priority: Self::get_priority(len, info, fee),2580			..Default::default()2581		})2582    }25832584    fn pre_dispatch(2585        self,2586        who: &Self::AccountId,2587        call: &Self::Call,2588        info: &DispatchInfoOf<Self::Call>,2589        len: usize,2590    ) -> Result<Self::Pre, TransactionValidityError> {2591        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2592        Ok((self.0, who.clone(), imbalance))2593    }25942595    fn post_dispatch(2596        pre: Self::Pre,2597        info: &DispatchInfoOf<Self::Call>,2598        post_info: &PostDispatchInfoOf<Self::Call>,2599        len: usize,2600        _result: &DispatchResult,2601    ) -> Result<(), TransactionValidityError> {2602		let (tip, who, imbalance) = pre;2603		let actual_fee = transaction_payment::Module::<T>::compute_actual_fee(2604			len as u32,2605			info,2606			post_info,2607			tip,2608		);2609		<T as transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2610		Ok(())2611    }2612}26132614// #endregion