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

difftreelog

source

pallets/nft/src/lib.rs97.5 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        /// Owned amount is lesser than tried to approve352        CantAfford,353        /// No approve found354        ApproveNotFound,355        /// Requested value more than approved.356        TokenValueNotEnough,357        /// Only approved addresses can call this method.358        ApproveRequired,359        /// Address is not in white list.360        AddresNotInWhiteList,361        /// Number of collection admins bound exceeded.362        CollectionAdminsLimitExceeded,363        /// Owned tokens by a single address bound exceeded.364        AddressOwnershipLimitExceeded,365        /// Length of items properties must be greater than 0.366        EmptyArgument,367        /// const_data exceeded data limit.368        TokenConstDataLimitExceeded,369        /// variable_data exceeded data limit.370        TokenVariableDataLimitExceeded,371        /// Not NFT item data used to mint in NFT collection.372        NotNftDataUsedToMintNftCollectionToken,373        /// Not Fungible item data used to mint in Fungible collection.374        NotFungibleDataUsedToMintFungibleCollectionToken,375        /// Not Re Fungible item data used to mint in Re Fungible collection.376        NotReFungibleDataUsedToMintReFungibleCollectionToken,377        /// Unexpected collection type.378        UnexpectedCollectionType,379        /// Can't store metadata in fungible tokens.380        CantStoreMetadataInFungibleTokens,381        /// Collection token limit exceeded382        CollectionTokenLimitExceeded,383        /// Account token limit exceeded per collection384        AccountTokenLimitExceeded,385        /// Collection limit bounds per collection exceeded386        CollectionLimitBoundsExceeded,387        /// Schema data size limit bound exceeded388        SchemaDataLimitExceeded,389        /// Maximum refungibility exceeded390        WrongRefungiblePieces391	}392}393394pub trait Config: system::Config + Sized + transaction_payment::Config + pallet_contracts::Config {395    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;396397    /// Weight information for extrinsics in this pallet.398	type WeightInfo: WeightInfo;399}400401#[cfg(feature = "runtime-benchmarks")]402mod benchmarking;403404// #endregion405406decl_storage! {407    trait Store for Module<T: Config> as Nft {408409        // Private members410        NextCollectionID: CollectionId;411        CreatedCollectionCount: u32;412        ChainVersion: u64;413        ItemListIndex: map hasher(identity) CollectionId => TokenId;414415        // Chain limits struct416        pub ChainLimit get(fn chain_limit) config(): ChainLimits;417418        // Bound counters419        CollectionCount: u32;420        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;421422        // Basic collections423        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;424        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;425        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;426427        /// Balance owner per collection map428        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;429430        /// second parameter: item id + owner account id + spender account id431        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;432433        /// Item collections434        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;435        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;436        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;437438        /// Index list439        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;440441        /// Tokens transfer baskets442        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;443        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;444        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;445        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;446447        // Contract Sponsorship and Ownership448        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;449        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;450        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;451        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;452        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 453        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 454    }455    add_extra_genesis {456        build(|config: &GenesisConfig<T>| {457            // Modification of storage458            for (_num, _c) in &config.collection {459                <Module<T>>::init_collection(_c);460            }461462            for (_num, _c, _i) in &config.nft_item_id {463                <Module<T>>::init_nft_token(*_c, _i);464            }465466            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {467                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);468            }469470            for (_num, _c, _i) in &config.refungible_item_id {471                <Module<T>>::init_refungible_token(*_c, _i);472            }473        })474    }475}476477decl_event!(478    pub enum Event<T>479    where480        AccountId = <T as system::Config>::AccountId,481    {482        /// New collection was created483        /// 484        /// # Arguments485        /// 486        /// * collection_id: Globally unique identifier of newly created collection.487        /// 488        /// * mode: [CollectionMode] converted into u8.489        /// 490        /// * account_id: Collection owner.491        Created(CollectionId, u8, AccountId),492493        /// New item was created.494        /// 495        /// # Arguments496        /// 497        /// * collection_id: Id of the collection where item was created.498        /// 499        /// * item_id: Id of an item. Unique within the collection.500        ItemCreated(CollectionId, TokenId),501502        /// Collection item was burned.503        /// 504        /// # Arguments505        /// 506        /// collection_id.507        /// 508        /// item_id: Identifier of burned NFT.509        ItemDestroyed(CollectionId, TokenId),510    }511);512513decl_module! {514    pub struct Module<T: Config> for enum Call 515    where 516        origin: T::Origin517    {518        fn deposit_event() = default;519        type Error = Error<T>;520521        fn on_initialize(now: T::BlockNumber) -> Weight {522523            if ChainVersion::get() < 2524            {525                let value = NextCollectionID::get();526                CreatedCollectionCount::put(value);527                ChainVersion::put(2);528            }529530            0531        }532533        /// 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.534        /// 535        /// # Permissions536        /// 537        /// * Anyone.538        /// 539        /// # Arguments540        /// 541        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.542        /// 543        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.544        /// 545        /// * token_prefix: UTF-8 string with token prefix.546        /// 547        /// * mode: [CollectionMode] collection type and type dependent data.548        // returns collection ID549        #[weight = <T as Config>::WeightInfo::create_collection()]550        pub fn create_collection(origin,551                                 collection_name: Vec<u16>,552                                 collection_description: Vec<u16>,553                                 token_prefix: Vec<u8>,554                                 mode: CollectionMode) -> DispatchResult {555556            // Anyone can create a collection557            let who = ensure_signed(origin)?;558559            let decimal_points = match mode {560                CollectionMode::Fungible(points) => points,561                _ => 0562            };563564            // bound Total number of collections565            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);566567            // check params568            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);569            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);570            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);571            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);572573            // Generate next collection ID574            let next_id = CreatedCollectionCount::get()575                .checked_add(1)576                .ok_or(Error::<T>::NumOverflow)?;577578            // bound counter579            let total = CollectionCount::get()580                .checked_add(1)581                .ok_or(Error::<T>::NumOverflow)?;582583            CreatedCollectionCount::put(next_id);584            CollectionCount::put(total);585586            // Create new collection587            let new_collection = CollectionType {588                owner: who.clone(),589                name: collection_name,590                mode: mode.clone(),591                mint_mode: false,592                access: AccessMode::Normal,593                description: collection_description,594                decimal_points: decimal_points,595                token_prefix: token_prefix,596                offchain_schema: Vec::new(),597                schema_version: SchemaVersion::ImageURL,598                sponsor: T::AccountId::default(),599                sponsor_confirmed: false,600                variable_on_chain_schema: Vec::new(),601                const_on_chain_schema: Vec::new(),602                limits: CollectionLimits::default()603            };604605            // Add new collection to map606            <Collection<T>>::insert(next_id, new_collection);607608            // call event609            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));610611            Ok(())612        }613614        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.615        /// 616        /// # Permissions617        /// 618        /// * Collection Owner.619        /// 620        /// # Arguments621        /// 622        /// * collection_id: collection to destroy.623        #[weight = <T as Config>::WeightInfo::destroy_collection()]624        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {625626            let sender = ensure_signed(origin)?;627            Self::check_owner_permissions(collection_id, sender)?;628629            <AddressTokens<T>>::remove_prefix(collection_id);630            <Allowances<T>>::remove_prefix(collection_id);631            <Balance<T>>::remove_prefix(collection_id);632            <ItemListIndex>::remove(collection_id);633            <AdminList<T>>::remove(collection_id);634            <Collection<T>>::remove(collection_id);635            <WhiteList<T>>::remove_prefix(collection_id);636637            <NftItemList<T>>::remove_prefix(collection_id);638            <FungibleItemList<T>>::remove_prefix(collection_id);639            <ReFungibleItemList<T>>::remove_prefix(collection_id);640641            <NftTransferBasket<T>>::remove_prefix(collection_id);642            <FungibleTransferBasket<T>>::remove_prefix(collection_id);643            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);644645            if CollectionCount::get() > 0646            {647                // bound couter648                let total = CollectionCount::get()649                    .checked_sub(1)650                    .ok_or(Error::<T>::NumOverflow)?;651652                CollectionCount::put(total);653            }654655            Ok(())656        }657658        /// Add an address to white list.659        /// 660        /// # Permissions661        /// 662        /// * Collection Owner663        /// * Collection Admin664        /// 665        /// # Arguments666        /// 667        /// * collection_id.668        /// 669        /// * address.670        #[weight = <T as Config>::WeightInfo::add_to_white_list()]671        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{672673            let sender = ensure_signed(origin)?;674            Self::check_owner_or_admin_permissions(collection_id, sender)?;675676            <WhiteList<T>>::insert(collection_id, address, true);677            678            Ok(())679        }680681        /// Remove an address from white list.682        /// 683        /// # Permissions684        /// 685        /// * Collection Owner686        /// * Collection Admin687        /// 688        /// # Arguments689        /// 690        /// * collection_id.691        /// 692        /// * address.693        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]694        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{695696            let sender = ensure_signed(origin)?;697            Self::check_owner_or_admin_permissions(collection_id, sender)?;698699            <WhiteList<T>>::remove(collection_id, address);700701            Ok(())702        }703704        /// Toggle between normal and white list access for the methods with access for `Anyone`.705        /// 706        /// # Permissions707        /// 708        /// * Collection Owner.709        /// 710        /// # Arguments711        /// 712        /// * collection_id.713        /// 714        /// * mode: [AccessMode]715        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]716        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult717        {718            let sender = ensure_signed(origin)?;719720            Self::check_owner_permissions(collection_id, sender)?;721            let mut target_collection = <Collection<T>>::get(collection_id);722            target_collection.access = mode;723            <Collection<T>>::insert(collection_id, target_collection);724725            Ok(())726        }727728        /// Allows Anyone to create tokens if:729        /// * White List is enabled, and730        /// * Address is added to white list, and731        /// * This method was called with True parameter732        /// 733        /// # Permissions734        /// * Collection Owner735        ///736        /// # Arguments737        /// 738        /// * collection_id.739        /// 740        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.741        #[weight = <T as Config>::WeightInfo::set_mint_permission()]742        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult743        {744            let sender = ensure_signed(origin)?;745746            Self::check_owner_permissions(collection_id, sender)?;747            let mut target_collection = <Collection<T>>::get(collection_id);748            target_collection.mint_mode = mint_permission;749            <Collection<T>>::insert(collection_id, target_collection);750751            Ok(())752        }753754        /// Change the owner of the collection.755        /// 756        /// # Permissions757        /// 758        /// * Collection Owner.759        /// 760        /// # Arguments761        /// 762        /// * collection_id.763        /// 764        /// * new_owner.765        #[weight = <T as Config>::WeightInfo::change_collection_owner()]766        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {767768            let sender = ensure_signed(origin)?;769            Self::check_owner_permissions(collection_id, sender)?;770            let mut target_collection = <Collection<T>>::get(collection_id);771            target_collection.owner = new_owner;772            <Collection<T>>::insert(collection_id, target_collection);773774            Ok(())775        }776777        /// Adds an admin of the Collection.778        /// 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. 779        /// 780        /// # Permissions781        /// 782        /// * Collection Owner.783        /// * Collection Admin.784        /// 785        /// # Arguments786        /// 787        /// * collection_id: ID of the Collection to add admin for.788        /// 789        /// * new_admin_id: Address of new admin to add.790        #[weight = <T as Config>::WeightInfo::add_collection_admin()]791        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {792793            let sender = ensure_signed(origin)?;794            Self::check_owner_or_admin_permissions(collection_id, sender)?;795            let mut admin_arr: Vec<T::AccountId> = Vec::new();796797            if <AdminList<T>>::contains_key(collection_id)798            {799                admin_arr = <AdminList<T>>::get(collection_id);800                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);801            }802803            // Number of collection admins804            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);805806            admin_arr.push(new_admin_id);807            <AdminList<T>>::insert(collection_id, admin_arr);808809            Ok(())810        }811812        /// 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.813        ///814        /// # Permissions815        /// 816        /// * Collection Owner.817        /// * Collection Admin.818        /// 819        /// # Arguments820        /// 821        /// * collection_id: ID of the Collection to remove admin for.822        /// 823        /// * account_id: Address of admin to remove.824        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]825        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {826827            let sender = ensure_signed(origin)?;828            Self::check_owner_or_admin_permissions(collection_id, sender)?;829            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);830831            let mut admin_arr = <AdminList<T>>::get(collection_id);832            admin_arr.retain(|i| *i != account_id);833            <AdminList<T>>::insert(collection_id, admin_arr);834835            Ok(())836        }837838        /// # Permissions839        /// 840        /// * Collection Owner841        /// 842        /// # Arguments843        /// 844        /// * collection_id.845        /// 846        /// * new_sponsor.847        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]848        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {849850            let sender = ensure_signed(origin)?;851            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);852853            let mut target_collection = <Collection<T>>::get(collection_id);854            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);855856            target_collection.sponsor = new_sponsor;857            target_collection.sponsor_confirmed = false;858            <Collection<T>>::insert(collection_id, target_collection);859860            Ok(())861        }862863        /// # Permissions864        /// 865        /// * Sponsor.866        /// 867        /// # Arguments868        /// 869        /// * collection_id.870        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]871        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {872873            let sender = ensure_signed(origin)?;874            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);875876            let mut target_collection = <Collection<T>>::get(collection_id);877            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);878879            target_collection.sponsor_confirmed = true;880            <Collection<T>>::insert(collection_id, target_collection);881882            Ok(())883        }884885        /// Switch back to pay-per-own-transaction model.886        ///887        /// # Permissions888        ///889        /// * Collection owner.890        /// 891        /// # Arguments892        /// 893        /// * collection_id.894        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]895        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {896897            let sender = ensure_signed(origin)?;898            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);899900            let mut target_collection = <Collection<T>>::get(collection_id);901            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);902903            target_collection.sponsor = T::AccountId::default();904            target_collection.sponsor_confirmed = false;905            <Collection<T>>::insert(collection_id, target_collection);906907            Ok(())908        }909910        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.911        /// 912        /// # Permissions913        /// 914        /// * Collection Owner.915        /// * Collection Admin.916        /// * Anyone if917        ///     * White List is enabled, and918        ///     * Address is added to white list, and919        ///     * MintPermission is enabled (see SetMintPermission method)920        /// 921        /// # Arguments922        /// 923        /// * collection_id: ID of the collection.924        /// 925        /// * owner: Address, initial owner of the NFT.926        ///927        /// * data: Token data to store on chain.928        // #[weight =929        // (130_000_000 as Weight)930        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))931        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))932        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]933934        #[weight = <T as Config>::WeightInfo::create_item(data.len())]935        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {936937            let sender = ensure_signed(origin)?;938939            Self::collection_exists(collection_id)?;940941            let target_collection = <Collection<T>>::get(collection_id);942943            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;944            Self::validate_create_item_args(&target_collection, &data)?;945            Self::create_item_no_validation(collection_id, owner, data)?;946947            Ok(())948        }949950        /// This method creates multiple instances of NFT Collection created with CreateCollection method.951        /// 952        /// # Permissions953        /// 954        /// * Collection Owner.955        /// * Collection Admin.956        /// * Anyone if957        ///     * White List is enabled, and958        ///     * Address is added to white list, and959        ///     * MintPermission is enabled (see SetMintPermission method)960        /// 961        /// # Arguments962        /// 963        /// * collection_id: ID of the collection.964        /// 965        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].966        /// 967        /// * owner: Address, initial owner of the NFT.968        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()969                               .map(|data| { data.len() })970                               .sum())]971        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {972973            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);974            let sender = ensure_signed(origin)?;975976            Self::collection_exists(collection_id)?;977            let target_collection = <Collection<T>>::get(collection_id);978979            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;980981            for data in &items_data {982                Self::validate_create_item_args(&target_collection, data)?;983            }984            for data in &items_data {985                Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?;986            }987988            Ok(())989        }990991        /// Destroys a concrete instance of NFT.992        /// 993        /// # Permissions994        /// 995        /// * Collection Owner.996        /// * Collection Admin.997        /// * Current NFT Owner.998        /// 999        /// # Arguments1000        /// 1001        /// * collection_id: ID of the collection.1002        /// 1003        /// * item_id: ID of NFT to burn.1004        #[weight = <T as Config>::WeightInfo::burn_item()]1005        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10061007            let sender = ensure_signed(origin)?;1008            Self::collection_exists(collection_id)?;10091010            // Transfer permissions check1011            let target_collection = <Collection<T>>::get(collection_id);1012            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1013                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1014                Error::<T>::NoPermission);10151016            if target_collection.access == AccessMode::WhiteList {1017                Self::check_white_list(collection_id, &sender)?;1018            }10191020            match target_collection.mode1021            {1022                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1023                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1024                CollectionMode::ReFungible  => Self::burn_refungible_item(collection_id, item_id, &sender)?,1025                _ => ()1026            };10271028            // call event1029            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10301031            Ok(())1032        }10331034        /// Change ownership of the token.1035        /// 1036        /// # Permissions1037        /// 1038        /// * Collection Owner1039        /// * Collection Admin1040        /// * Current NFT owner1041        ///1042        /// # Arguments1043        /// 1044        /// * recipient: Address of token recipient.1045        /// 1046        /// * collection_id.1047        /// 1048        /// * item_id: ID of the item1049        ///     * Non-Fungible Mode: Required.1050        ///     * Fungible Mode: Ignored.1051        ///     * Re-Fungible Mode: Required.1052        /// 1053        /// * value: Amount to transfer.1054        ///     * Non-Fungible Mode: Ignored1055        ///     * Fungible Mode: Must specify transferred amount1056        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1057        #[weight = <T as Config>::WeightInfo::transfer()]1058        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1059            let sender = ensure_signed(origin)?;1060            Self::transfer_internal(sender, recipient, collection_id, item_id, value)1061        }10621063        /// Set, change, or remove approved address to transfer the ownership of the NFT.1064        /// 1065        /// # Permissions1066        /// 1067        /// * Collection Owner1068        /// * Collection Admin1069        /// * Current NFT owner1070        /// 1071        /// # Arguments1072        /// 1073        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1074        /// 1075        /// * collection_id.1076        /// 1077        /// * item_id: ID of the item.1078        #[weight = <T as Config>::WeightInfo::approve()]1079        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {10801081            let sender = ensure_signed(origin)?;10821083            Self::collection_exists(collection_id)?;1084            Self::token_exists(collection_id, item_id, &sender)?;10851086            // Transfer permissions check1087            let target_collection = <Collection<T>>::get(collection_id);1088            let allowance_limit = if Self::is_owner_or_admin_permissions(1089                collection_id,1090                sender.clone(),1091            ) {1092                None1093            } else if let Some(amount) = Self::owned_amount(1094                sender.clone(),1095                collection_id,1096                item_id,1097            ) {1098                Some(amount)1099            } else {1100                fail!(Error::<T>::NoPermission);1101            };11021103            if target_collection.access == AccessMode::WhiteList {1104                Self::check_white_list(collection_id, &sender)?;1105                Self::check_white_list(collection_id, &spender)?;1106            }11071108            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1109            let mut allowance: u128 = amount;1110            if allowance_exists {1111                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1112            }1113            if let Some(limit) = allowance_limit {1114                ensure!(limit >= allowance, Error::<T>::CantAfford);1115            }1116            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11171118            Ok(())1119        }1120        1121        /// 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.1122        /// 1123        /// # Permissions1124        /// * Collection Owner1125        /// * Collection Admin1126        /// * Current NFT owner1127        /// * Address approved by current NFT owner1128        /// 1129        /// # Arguments1130        /// 1131        /// * from: Address that owns token.1132        /// 1133        /// * recipient: Address of token recipient.1134        /// 1135        /// * collection_id.1136        /// 1137        /// * item_id: ID of the item.1138        /// 1139        /// * value: Amount to transfer.1140        #[weight = <T as Config>::WeightInfo::transfer_from()]1141        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11421143            let sender = ensure_signed(origin)?;1144            let mut appoved_transfer = false;11451146            // Check approval1147            let mut approval: u128 = 0;1148            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1149                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1150                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1151                appoved_transfer = true;1152            }11531154            let target_collection = <Collection<T>>::get(collection_id);11551156            // Limits check1157            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11581159            // Transfer permissions check         1160            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1161                Error::<T>::NoPermission);11621163            if target_collection.access == AccessMode::WhiteList {1164                Self::check_white_list(collection_id, &sender)?;1165                Self::check_white_list(collection_id, &recipient)?;1166            }11671168            // Reduce approval by transferred amount or remove if remaining approval drops to 01169            if approval.checked_sub(value).unwrap_or(0) > 0 {1170                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1171            }1172            else {1173                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1174            }11751176            match target_collection.mode1177            {1178                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1179                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1180                CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1181                _ => ()1182            };11831184            Ok(())1185        }11861187        // #[weight = 0]1188        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11891190        //     // let no_perm_mes = "You do not have permissions to modify this collection";1191        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1192        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1193        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11941195        //     // // on_nft_received  call11961197        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;11981199        //     Ok(())1200        // }12011202        /// Set off-chain data schema.1203        /// 1204        /// # Permissions1205        /// 1206        /// * Collection Owner1207        /// * Collection Admin1208        /// 1209        /// # Arguments1210        /// 1211        /// * collection_id.1212        /// 1213        /// * schema: String representing the offchain data schema.1214        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1215        pub fn set_variable_meta_data (1216            origin,1217            collection_id: CollectionId,1218            item_id: TokenId,1219            data: Vec<u8>1220        ) -> DispatchResult {1221            let sender = ensure_signed(origin)?;1222            1223            Self::collection_exists(collection_id)?;1224            Self::token_exists(collection_id, item_id, &sender)?;12251226            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12271228            // Modify permissions check1229            let target_collection = <Collection<T>>::get(collection_id);1230            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1231                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1232                Error::<T>::NoPermission);12331234            match target_collection.mode1235            {1236                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1237                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1238                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1239                _ => fail!(Error::<T>::UnexpectedCollectionType)1240            };12411242            Ok(())1243        }1244 1245        /// Set schema standard1246        /// ImageURL1247        /// Unique1248        /// 1249        /// # Permissions1250        /// 1251        /// * Collection Owner1252        /// * Collection Admin1253        /// 1254        /// # Arguments1255        /// 1256        /// * collection_id.1257        /// 1258        /// * schema: SchemaVersion: enum1259        #[weight = <T as Config>::WeightInfo::set_schema_version()]1260        pub fn set_schema_version(1261            origin,1262            collection_id: CollectionId,1263            version: SchemaVersion1264        ) -> DispatchResult {1265            let sender = ensure_signed(origin)?;1266            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1267            let mut target_collection = <Collection<T>>::get(collection_id);1268            target_collection.schema_version = version;1269            <Collection<T>>::insert(collection_id, target_collection);12701271            Ok(())1272        }12731274        /// Set off-chain data schema.1275        /// 1276        /// # Permissions1277        /// 1278        /// * Collection Owner1279        /// * Collection Admin1280        /// 1281        /// # Arguments1282        /// 1283        /// * collection_id.1284        /// 1285        /// * schema: String representing the offchain data schema.1286        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1287        pub fn set_offchain_schema(1288            origin,1289            collection_id: CollectionId,1290            schema: Vec<u8>1291        ) -> DispatchResult {1292            let sender = ensure_signed(origin)?;1293            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12941295            // check schema limit1296            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");12971298            let mut target_collection = <Collection<T>>::get(collection_id);1299            target_collection.offchain_schema = schema;1300            <Collection<T>>::insert(collection_id, target_collection);13011302            Ok(())1303        }13041305        /// Set const on-chain data schema.1306        /// 1307        /// # Permissions1308        /// 1309        /// * Collection Owner1310        /// * Collection Admin1311        /// 1312        /// # Arguments1313        /// 1314        /// * collection_id.1315        /// 1316        /// * schema: String representing the const on-chain data schema.1317        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1318        pub fn set_const_on_chain_schema (1319            origin,1320            collection_id: CollectionId,1321            schema: Vec<u8>1322        ) -> DispatchResult {1323            let sender = ensure_signed(origin)?;1324            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13251326            // check schema limit1327            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");13281329            let mut target_collection = <Collection<T>>::get(collection_id);1330            target_collection.const_on_chain_schema = schema;1331            <Collection<T>>::insert(collection_id, target_collection);13321333            Ok(())1334        }13351336        /// Set variable on-chain data schema.1337        /// 1338        /// # Permissions1339        /// 1340        /// * Collection Owner1341        /// * Collection Admin1342        /// 1343        /// # Arguments1344        /// 1345        /// * collection_id.1346        /// 1347        /// * schema: String representing the variable on-chain data schema.1348        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1349        pub fn set_variable_on_chain_schema (1350            origin,1351            collection_id: CollectionId,1352            schema: Vec<u8>1353        ) -> DispatchResult {1354            let sender = ensure_signed(origin)?;1355            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13561357            // check schema limit1358            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");13591360            let mut target_collection = <Collection<T>>::get(collection_id);1361            target_collection.variable_on_chain_schema = schema;1362            <Collection<T>>::insert(collection_id, target_collection);13631364            Ok(())1365        }13661367        // Sudo permissions function1368        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1369        pub fn set_chain_limits(1370            origin,1371            limits: ChainLimits1372        ) -> DispatchResult {13731374            #[cfg(not(feature = "runtime-benchmarks"))]1375            ensure_root(origin)?;13761377            <ChainLimit>::put(limits);1378            Ok(())1379        }13801381        /// Enable smart contract self-sponsoring.1382        /// 1383        /// # Permissions1384        /// 1385        /// * Contract Owner1386        /// 1387        /// # Arguments1388        /// 1389        /// * contract address1390        /// * enable flag1391        /// 1392        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1393        pub fn enable_contract_sponsoring(1394            origin,1395            contract_address: T::AccountId,1396            enable: bool1397        ) -> DispatchResult {13981399            let sender = ensure_signed(origin)?;14001401            #[cfg(feature = "runtime-benchmarks")]1402            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14031404            Self::ensure_contract_owned(sender, &contract_address)?;14051406            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1407            Ok(())1408        }14091410        /// Set the rate limit for contract sponsoring to specified number of blocks.1411        /// 1412        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1413        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1414        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1415        /// from contract endowment if there are at least B blocks between such transactions. 1416        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1417        /// 1418        /// # Permissions1419        /// 1420        /// * Contract Owner1421        /// 1422        /// # Arguments1423        /// 1424        /// -`contract_address`: Address of the contract to sponsor1425        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1426        /// 1427        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1428        pub fn set_contract_sponsoring_rate_limit(1429            origin,1430            contract_address: T::AccountId,1431            rate_limit: T::BlockNumber1432        ) -> DispatchResult {1433            let sender = ensure_signed(origin)?;14341435            #[cfg(feature = "runtime-benchmarks")]1436            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14371438            Self::ensure_contract_owned(sender, &contract_address)?;1439            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1440            Ok(())1441        }14421443        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1444        /// 1445        /// # Permissions1446        /// 1447        /// * Address that deployed smart contract.1448        /// 1449        /// # Arguments1450        /// 1451        /// -`contract_address`: Address of the contract.1452        /// 1453        /// - `enable`: .  1454        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1455        pub fn toggle_contract_white_list(1456            origin,1457            contract_address: T::AccountId,1458            enable: bool1459        ) -> DispatchResult {1460            let sender = ensure_signed(origin)?;14611462            #[cfg(feature = "runtime-benchmarks")]1463            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14641465            Self::ensure_contract_owned(sender, &contract_address)?;1466            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1467            Ok(())1468        }1469        1470        /// Add an address to smart contract white list.1471        /// 1472        /// # Permissions1473        /// 1474        /// * Address that deployed smart contract.1475        /// 1476        /// # Arguments1477        /// 1478        /// -`contract_address`: Address of the contract.1479        ///1480        /// -`account_address`: Address to add.1481        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1482        pub fn add_to_contract_white_list(1483            origin,1484            contract_address: T::AccountId,1485            account_address: T::AccountId1486        ) -> DispatchResult {1487            let sender = ensure_signed(origin)?;14881489            #[cfg(feature = "runtime-benchmarks")]1490            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1491            1492            Self::ensure_contract_owned(sender, &contract_address)?;      1493            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1494            Ok(())1495        }14961497        /// Remove an address from smart contract white list.1498        /// 1499        /// # Permissions1500        /// 1501        /// * Address that deployed smart contract.1502        /// 1503        /// # Arguments1504        /// 1505        /// -`contract_address`: Address of the contract.1506        ///1507        /// -`account_address`: Address to remove.1508        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1509        pub fn remove_from_contract_white_list(1510            origin,1511            contract_address: T::AccountId,1512            account_address: T::AccountId1513        ) -> DispatchResult {1514            let sender = ensure_signed(origin)?;15151516            #[cfg(feature = "runtime-benchmarks")]1517            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15181519            Self::ensure_contract_owned(sender, &contract_address)?;1520            <ContractWhiteList<T>>::remove(contract_address, account_address);1521            Ok(())1522        }15231524        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1525        pub fn set_collection_limits(1526            origin,1527            collection_id: u32,1528            limits: CollectionLimits,1529        ) -> DispatchResult {1530            let sender = ensure_signed(origin)?;1531            Self::check_owner_permissions(collection_id, sender.clone())?;1532            let mut target_collection = <Collection<T>>::get(collection_id);1533            let chain_limits = ChainLimit::get();1534            let climits = target_collection.limits;15351536            // collection bounds1537            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1538                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1539                Error::<T>::CollectionLimitBoundsExceeded);15401541            // token_limit   check  prev1542            ensure!(climits.token_limit > limits.token_limit && 1543                limits.token_limit <= chain_limits.account_token_ownership_limit, 1544                Error::<T>::AccountTokenLimitExceeded);15451546            target_collection.limits = limits;1547            <Collection<T>>::insert(collection_id, target_collection);15481549            Ok(())1550        } 1551    }1552}15531554impl<T: Config> Module<T> {15551556    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {15571558        let target_collection = <Collection<T>>::get(collection_id);15591560        // Limits check1561        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;15621563        // Transfer permissions check1564        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1565            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1566            Error::<T>::NoPermission);15671568        if target_collection.access == AccessMode::WhiteList {1569            Self::check_white_list(collection_id, &sender)?;1570            Self::check_white_list(collection_id, &recipient)?;1571        }15721573        match target_collection.mode1574        {1575            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1576            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1577            CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1578            _ => ()1579        };15801581        Ok(())1582    }158315841585    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15861587        // check token limit and account token limit1588        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1589        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1590        1591        Ok(())1592    }15931594    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15951596        // check token limit and account token limit1597        let total_items: u32 = ItemListIndex::get(collection_id);1598        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1599        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1600        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);16011602        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1603            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1604            Self::check_white_list(collection_id, owner)?;1605            Self::check_white_list(collection_id, sender)?;1606        }16071608        Ok(())1609    }16101611    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1612        match target_collection.mode1613        {1614            CollectionMode::NFT => {1615                if let CreateItemData::NFT(data) = data {1616                    // 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);1619                } else {1620                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1621                }1622            },1623            CollectionMode::Fungible(_) => {1624                if let CreateItemData::Fungible(_) = data {1625                } else {1626                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1627                }1628            },1629            CollectionMode::ReFungible => {1630                if let CreateItemData::ReFungible(data) = data {16311632                    // check sizes1633                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1634                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16351636                    // Check refungibility limits1637                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1638                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1639                } else {1640                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1641                }1642            },1643            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1644        };16451646        Ok(())1647    }16481649    fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1650        match data1651        {1652            CreateItemData::NFT(data) => {1653                let item = NftItemType {1654                    owner,1655                    const_data: data.const_data,1656                    variable_data: data.variable_data1657                };16581659                Self::add_nft_item(collection_id, item)?;1660            },1661            CreateItemData::Fungible(data) => {1662                Self::add_fungible_item(collection_id, &owner, data.value)?;1663            },1664            CreateItemData::ReFungible(data) => {1665                let mut owner_list = Vec::new();1666                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});16671668                let item = ReFungibleItemType {1669                    owner: owner_list,1670                    const_data: data.const_data,1671                    variable_data: data.variable_data1672                };16731674                Self::add_refungible_item(collection_id, item)?;1675            }1676        };16771678        // call event1679        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16801681        Ok(())1682    }16831684    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {16851686        // Does new owner already have an account?1687        let mut balance: u128 = 0;1688        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1689            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1690        } 16911692        // Mint 1693        let item = FungibleItemType {1694            value: balance + value1695        };1696        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16971698        // Update balance1699        let new_balance = <Balance<T>>::get(collection_id, owner)1700            .checked_add(value)1701            .ok_or(Error::<T>::NumOverflow)?;1702        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17031704        Ok(())1705    }17061707    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1708        let current_index = <ItemListIndex>::get(collection_id)1709            .checked_add(1)1710            .ok_or(Error::<T>::NumOverflow)?;1711        let itemcopy = item.clone();17121713        let value = item.owner.first().unwrap().fraction;1714        let owner = item.owner.first().unwrap().owner.clone();17151716        Self::add_token_index(collection_id, current_index, &owner)?;17171718        <ItemListIndex>::insert(collection_id, current_index);1719        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17201721        // Update balance1722        let new_balance = <Balance<T>>::get(collection_id, &owner)1723            .checked_add(value)1724            .ok_or(Error::<T>::NumOverflow)?;1725        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17261727        Ok(())1728    }17291730    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1731        let current_index = <ItemListIndex>::get(collection_id)1732            .checked_add(1)1733            .ok_or(Error::<T>::NumOverflow)?;17341735        let item_owner = item.owner.clone();1736        Self::add_token_index(collection_id, current_index, &item.owner)?;17371738        <ItemListIndex>::insert(collection_id, current_index);1739        <NftItemList<T>>::insert(collection_id, current_index, item);17401741        // Update balance1742        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1743            .checked_add(1)1744            .ok_or(Error::<T>::NumOverflow)?;1745        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17461747        Ok(())1748    }17491750    fn burn_refungible_item(1751        collection_id: CollectionId,1752        item_id: TokenId,1753        owner: &T::AccountId,1754    ) -> DispatchResult {1755        ensure!(1756            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1757            Error::<T>::TokenNotFound1758        );1759        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1760        let rft_balance = token1761            .owner1762            .iter()1763            .filter(|&i| i.owner == *owner)1764            .next()1765            .unwrap();1766        Self::remove_token_index(collection_id, item_id, owner)?;17671768        // update balance1769        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1770            .checked_sub(rft_balance.fraction)1771            .ok_or(Error::<T>::NumOverflow)?;1772        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);17731774        // Re-create owners list with sender removed1775        let index = token1776            .owner1777            .iter()1778            .position(|i| i.owner == *owner)1779            .unwrap();1780        token.owner.remove(index);1781        let owner_count = token.owner.len();17821783        // Burn the token completely if this was the last (only) owner1784        if owner_count == 0 {1785            <ReFungibleItemList<T>>::remove(collection_id, item_id);1786        }1787        else {1788            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1789        }17901791        Ok(())1792    }17931794    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1795        ensure!(1796            <NftItemList<T>>::contains_key(collection_id, item_id),1797            Error::<T>::TokenNotFound1798        );1799        let item = <NftItemList<T>>::get(collection_id, item_id);1800        Self::remove_token_index(collection_id, item_id, &item.owner)?;18011802        // update balance1803        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1804            .checked_sub(1)1805            .ok_or(Error::<T>::NumOverflow)?;1806        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1807        <NftItemList<T>>::remove(collection_id, item_id);18081809        Ok(())1810    }18111812    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1813        ensure!(1814            <FungibleItemList<T>>::contains_key(collection_id, owner),1815            Error::<T>::TokenNotFound1816        );1817        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1818        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18191820        // update balance1821        let new_balance = <Balance<T>>::get(collection_id, owner)1822            .checked_sub(value)1823            .ok_or(Error::<T>::NumOverflow)?;1824        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18251826        if balance.value - value > 0 {1827            balance.value -= value;1828            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1829        }1830        else {1831            <FungibleItemList<T>>::remove(collection_id, owner);1832        }18331834        Ok(())1835    }18361837    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1838        ensure!(1839            <Collection<T>>::contains_key(collection_id),1840            Error::<T>::CollectionNotFound1841        );1842        Ok(())1843    }18441845    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1846        Self::collection_exists(collection_id)?;18471848        let target_collection = <Collection<T>>::get(collection_id);1849        ensure!(1850            subject == target_collection.owner,1851            Error::<T>::NoPermission1852        );18531854        Ok(())1855    }18561857    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1858        let target_collection = <Collection<T>>::get(collection_id);1859        let mut result: bool = subject == target_collection.owner;1860        let exists = <AdminList<T>>::contains_key(collection_id);18611862        if !result & exists {1863            if <AdminList<T>>::get(collection_id).contains(&subject) {1864                result = true1865            }1866        }18671868        result1869    }18701871    fn check_owner_or_admin_permissions(1872        collection_id: CollectionId,1873        subject: T::AccountId,1874    ) -> DispatchResult {1875        Self::collection_exists(collection_id)?;1876        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18771878        ensure!(1879            result,1880            Error::<T>::NoPermission1881        );1882        Ok(())1883    }18841885    fn owned_amount(1886        subject: T::AccountId,1887        collection_id: CollectionId,1888        item_id: TokenId,1889    ) -> Option<u128> {1890        let target_collection = <Collection<T>>::get(collection_id);18911892        match target_collection.mode {1893            CollectionMode::NFT => {1894                if <NftItemList<T>>::get(collection_id, item_id).owner == subject {1895                    return Some(1)1896                }1897                None1898            },1899            CollectionMode::Fungible(_) => {1900                if <FungibleItemList<T>>::contains_key(collection_id, &subject) {1901                    return Some(<FungibleItemList<T>>::get(collection_id, &subject)1902                        .value);1903                }1904                None1905            },1906            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)1907                .owner1908                .iter()1909                .find(|i| i.owner == subject)1910                .map(|i| i.fraction),1911            CollectionMode::Invalid => None,1912        }1913    }19141915    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1916        let target_collection = <Collection<T>>::get(collection_id);19171918        match target_collection.mode {1919            CollectionMode::NFT => {1920                <NftItemList<T>>::get(collection_id, item_id).owner == subject1921            }1922            CollectionMode::Fungible(_) => {1923                <FungibleItemList<T>>::contains_key(collection_id, &subject)1924            }1925            CollectionMode::ReFungible => {1926                <ReFungibleItemList<T>>::get(collection_id, item_id)1927                    .owner1928                    .iter()1929                    .any(|i| i.owner == subject)1930            }1931            CollectionMode::Invalid => false,1932        }1933    }19341935    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1936        let mes = Error::<T>::AddresNotInWhiteList;1937        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);19381939        Ok(())1940    }19411942    /// Check if token exists. In case of Fungible, check if there is an entry for 1943    /// the owner in fungible balances double map1944    fn token_exists(1945        collection_id: CollectionId,1946        item_id: TokenId,1947        owner: &T::AccountId1948    ) -> DispatchResult {1949        let target_collection = <Collection<T>>::get(collection_id);1950        let exists = match target_collection.mode1951        {1952            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1953            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),1954            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1955            _ => false1956        };19571958        ensure!(exists == true, Error::<T>::TokenNotFound);1959        Ok(())1960    }19611962    fn transfer_fungible(1963        collection_id: CollectionId,1964        value: u128,1965        owner: &T::AccountId,1966        recipient: &T::AccountId,1967    ) -> DispatchResult {1968        Self::token_exists(collection_id, 0, owner)?;19691970        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1971        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19721973        // Send balance to recipient (updates balanceOf of recipient)1974        Self::add_fungible_item(collection_id, recipient, value)?;19751976        // update balanceOf of sender1977        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);19781979        // Reduce or remove sender1980        if balance.value == value {1981            <FungibleItemList<T>>::remove(collection_id, owner);1982        }1983        else {1984            balance.value -= value;1985            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1986        }19871988        Ok(())1989    }19901991    fn transfer_refungible(1992        collection_id: CollectionId,1993        item_id: TokenId,1994        value: u128,1995        owner: T::AccountId,1996        new_owner: T::AccountId,1997    ) -> DispatchResult {1998        Self::token_exists(collection_id, item_id, &owner)?;19992000        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);2001        let item = full_item2002            .owner2003            .iter()2004            .filter(|i| i.owner == owner)2005            .next()2006            .ok_or(Error::<T>::NumOverflow)?;2007        let amount = item.fraction;20082009        ensure!(amount >= value, Error::<T>::TokenValueTooLow);20102011        // update balance2012        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2013            .checked_sub(value)2014            .ok_or(Error::<T>::NumOverflow)?;2015        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20162017        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2018            .checked_add(value)2019            .ok_or(Error::<T>::NumOverflow)?;2020        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20212022        let old_owner = item.owner.clone();2023        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20242025        // transfer2026        if amount == value && !new_owner_has_account {2027            // change owner2028            // new owner do not have account2029            let mut new_full_item = full_item.clone();2030            new_full_item2031                .owner2032                .iter_mut()2033                .find(|i| i.owner == owner)2034                .unwrap()2035                .owner = new_owner.clone();2036            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20372038            // update index collection2039            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2040        } else {2041            let mut new_full_item = full_item.clone();2042            new_full_item2043                .owner2044                .iter_mut()2045                .find(|i| i.owner == owner)2046                .unwrap()2047                .fraction -= value;20482049            // separate amount2050            if new_owner_has_account {2051                // new owner has account2052                new_full_item2053                    .owner2054                    .iter_mut()2055                    .find(|i| i.owner == new_owner)2056                    .unwrap()2057                    .fraction += value;2058            } else {2059                // new owner do not have account2060                new_full_item.owner.push(Ownership {2061                    owner: new_owner.clone(),2062                    fraction: value,2063                });2064                Self::add_token_index(collection_id, item_id, &new_owner)?;2065            }20662067            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2068        }20692070        Ok(())2071    }20722073    fn transfer_nft(2074        collection_id: CollectionId,2075        item_id: TokenId,2076        sender: T::AccountId,2077        new_owner: T::AccountId,2078    ) -> DispatchResult {2079        Self::token_exists(collection_id, item_id, &sender)?;20802081        let mut item = <NftItemList<T>>::get(collection_id, item_id);20822083        ensure!(2084            sender == item.owner,2085            Error::<T>::MustBeTokenOwner2086        );20872088        // update balance2089        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2090            .checked_sub(1)2091            .ok_or(Error::<T>::NumOverflow)?;2092        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20932094        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2095            .checked_add(1)2096            .ok_or(Error::<T>::NumOverflow)?;2097        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20982099        // change owner2100        let old_owner = item.owner.clone();2101        item.owner = new_owner.clone();2102        <NftItemList<T>>::insert(collection_id, item_id, item);21032104        // update index collection2105        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21062107        Ok(())2108    }2109    2110    fn set_re_fungible_variable_data(2111        collection_id: CollectionId,2112        item_id: TokenId,2113        data: Vec<u8>2114    ) -> DispatchResult {2115        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);21162117        item.variable_data = data;21182119        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21202121        Ok(())2122    }21232124    fn set_nft_variable_data(2125        collection_id: CollectionId,2126        item_id: TokenId,2127        data: Vec<u8>2128    ) -> DispatchResult {2129        let mut item = <NftItemList<T>>::get(collection_id, item_id);2130        2131        item.variable_data = data;21322133        <NftItemList<T>>::insert(collection_id, item_id, item);2134        2135        Ok(())2136    }21372138    fn init_collection(item: &CollectionType<T::AccountId>) {2139        // check params2140        assert!(2141            item.decimal_points <= MAX_DECIMAL_POINTS,2142            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2143        );2144        assert!(2145            item.name.len() <= 64,2146            "Collection name can not be longer than 63 char"2147        );2148        assert!(2149            item.name.len() <= 256,2150            "Collection description can not be longer than 255 char"2151        );2152        assert!(2153            item.token_prefix.len() <= 16,2154            "Token prefix can not be longer than 15 char"2155        );21562157        // Generate next collection ID2158        let next_id = CreatedCollectionCount::get()2159            .checked_add(1)2160            .unwrap();21612162        CreatedCollectionCount::put(next_id);2163    }21642165    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2166        let current_index = <ItemListIndex>::get(collection_id)2167            .checked_add(1)2168            .unwrap();21692170        let item_owner = item.owner.clone();2171        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21722173        <ItemListIndex>::insert(collection_id, current_index);21742175        // Update balance2176        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2177            .checked_add(1)2178            .unwrap();2179        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2180    }21812182    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2183        let current_index = <ItemListIndex>::get(collection_id)2184            .checked_add(1)2185            .unwrap();21862187        Self::add_token_index(collection_id, current_index, owner).unwrap();21882189        <ItemListIndex>::insert(collection_id, current_index);21902191        // Update balance2192        let new_balance = <Balance<T>>::get(collection_id, owner)2193            .checked_add(item.value)2194            .unwrap();2195        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2196    }21972198    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2199        let current_index = <ItemListIndex>::get(collection_id)2200            .checked_add(1)2201            .unwrap();22022203        let value = item.owner.first().unwrap().fraction;2204        let owner = item.owner.first().unwrap().owner.clone();22052206        Self::add_token_index(collection_id, current_index, &owner).unwrap();22072208        <ItemListIndex>::insert(collection_id, current_index);22092210        // Update balance2211        let new_balance = <Balance<T>>::get(collection_id, &owner)2212            .checked_add(value)2213            .unwrap();2214        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2215    }22162217    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {22182219        // add to account limit2220        if <AccountItemCount<T>>::contains_key(owner) {22212222            // bound Owned tokens by a single address2223            let count = <AccountItemCount<T>>::get(owner);2224            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);22252226            <AccountItemCount<T>>::insert(owner.clone(), count2227                .checked_add(1)2228                .ok_or(Error::<T>::NumOverflow)?);2229        }2230        else {2231            <AccountItemCount<T>>::insert(owner.clone(), 1);2232        }22332234        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2235        if list_exists {2236            let mut list = <AddressTokens<T>>::get(collection_id, owner);2237            let item_contains = list.contains(&item_index.clone());22382239            if !item_contains {2240                list.push(item_index.clone());2241            }22422243            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2244        } else {2245            let mut itm = Vec::new();2246            itm.push(item_index.clone());2247            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2248        }22492250        Ok(())2251    }22522253    fn remove_token_index(2254        collection_id: CollectionId,2255        item_index: TokenId,2256        owner: &T::AccountId,2257    ) -> DispatchResult {22582259        // update counter2260        <AccountItemCount<T>>::insert(owner.clone(), 2261            <AccountItemCount<T>>::get(owner)2262            .checked_sub(1)2263            .ok_or(Error::<T>::NumOverflow)?);226422652266        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2267        if list_exists {2268            let mut list = <AddressTokens<T>>::get(collection_id, owner);2269            let item_contains = list.contains(&item_index.clone());22702271            if item_contains {2272                list.retain(|&item| item != item_index);2273                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2274            }2275        }22762277        Ok(())2278    }22792280    fn move_token_index(2281        collection_id: CollectionId,2282        item_index: TokenId,2283        old_owner: &T::AccountId,2284        new_owner: &T::AccountId,2285    ) -> DispatchResult {2286        Self::remove_token_index(collection_id, item_index, old_owner)?;2287        Self::add_token_index(collection_id, item_index, new_owner)?;22882289        Ok(())2290    }2291    2292    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2293        if <ContractOwner<T>>::contains_key(contract.clone()) {2294            let owner = <ContractOwner<T>>::get(contract);2295            ensure!(account == owner, Error::<T>::NoPermission);2296        } else {2297            fail!(Error::<T>::NoPermission);2298        }22992300        Ok(())2301    }2302}23032304////////////////////////////////////////////////////////////////////////////////////////////////////2305// Economic models2306// #region23072308/// Fee multiplier.2309pub type Multiplier = FixedU128;23102311type BalanceOf<T> = <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::Balance;23122313/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2314/// in the queue.2315#[derive(Encode, Decode, Clone, Eq, PartialEq)]2316pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);23172318impl<T: Config + Send + Sync> sp_std::fmt::Debug 2319    for ChargeTransactionPayment<T>2320{2321	#[cfg(feature = "std")]2322	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2323		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2324	}2325	#[cfg(not(feature = "std"))]2326	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2327		Ok(())2328	}2329}23302331impl<T: Config> ChargeTransactionPayment<T>2332where2333    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2334    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2335    T::AccountId: AsRef<[u8]>,2336    T::AccountId: UncheckedFrom<T::Hash>,2337{2338    fn traditional_fee(2339        len: usize,2340        info: &DispatchInfoOf<T::Call>,2341        tip: BalanceOf<T>,2342    ) -> BalanceOf<T>2343    where2344        T::Call: Dispatchable<Info = DispatchInfo>,2345    {2346        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2347    }23482349	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2350        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2351        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2352        let len_saturation = max_block_length as u64 / (len as u64).max(1);2353        let coefficient: BalanceOf<T> = weight_saturation2354            .min(len_saturation)2355            .saturated_into::<BalanceOf<T>>();2356        final_fee2357            .saturating_mul(coefficient)2358            .saturated_into::<TransactionPriority>()2359    }23602361    fn withdraw_fee(2362        &self,2363        who: &T::AccountId,2364        call: &T::Call,2365        info: &DispatchInfoOf<T::Call>,2366        len: usize,2367	) -> Result<2368		(2369			BalanceOf<T>,2370			<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2371		),2372		TransactionValidityError,2373	> {2374        let tip = self.0;23752376        // Set fee based on call type. Creating collection costs 1 Unique.2377        // All other transactions have traditional fees so far2378        // let fee = match call.is_sub_type() {2379        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2380        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2381        //                                                 // _ => <BalanceOf<T>>::from(100)2382        // };2383        let fee = Self::traditional_fee(len, info, tip);23842385        // Only mess with balances if fee is not zero.2386        if fee.is_zero() {2387            return <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2388			.map(|i| (fee, i));2389        }23902391        // Determine who is paying transaction fee based on ecnomic model2392        // Parse call to extract collection ID and access collection sponsor2393        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2394            Some(Call::create_item(collection_id, _owner, _properties)) => {23952396                // sponsor timeout2397                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;23982399                let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;2400                let mut sponsored = true;2401                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2402                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2403                    let limit_time = last_tx_block + limit.into();2404                    if block_number <= limit_time {2405                        sponsored = false;2406                    }2407                }2408                if sponsored {2409                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2410                }24112412                // check free create limit2413                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2414                   (<Collection<T>>::get(collection_id).sponsor_confirmed) &&2415                   (sponsored)2416                {2417                    <Collection<T>>::get(collection_id).sponsor2418                } else {2419                    T::AccountId::default()2420                }2421            }2422            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2423                2424                let mut sponsor_transfer = false;2425                if <Collection<T>>::get(collection_id).sponsor_confirmed {24262427                    let collection_limits = <Collection<T>>::get(collection_id).limits;2428                    let collection_mode = <Collection<T>>::get(collection_id).mode;2429    2430                    // sponsor timeout2431                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2432                    sponsor_transfer = match collection_mode {2433                        CollectionMode::NFT => {2434    2435                            // get correct limit2436                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2437                                collection_limits.sponsor_transfer_timeout2438                            } else {2439                                ChainLimit::get().nft_sponsor_transfer_timeout2440                            };2441    2442                            let mut sponsored = true;2443                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2444                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2445                                let limit_time = last_tx_block + limit.into();2446                                if block_number <= limit_time {2447                                    sponsored = false;2448                                }2449                            }2450                            if sponsored {2451                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2452                            }24532454                            sponsored2455                        }2456                        CollectionMode::Fungible(_) => {2457    2458                            // get correct limit2459                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2460                                collection_limits.sponsor_transfer_timeout2461                            } else {2462                                ChainLimit::get().fungible_sponsor_transfer_timeout2463                            };2464    2465                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2466                            let mut sponsored = true;2467                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2468                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2469                                let limit_time = last_tx_block + limit.into();2470                                if block_number <= limit_time {2471                                    sponsored = false;2472                                }2473                            }2474                            if sponsored {2475                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2476                            }24772478                            sponsored2479                        }2480                        CollectionMode::ReFungible => {2481    2482                            // get correct limit2483                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2484                                collection_limits.sponsor_transfer_timeout2485                            } else {2486                                ChainLimit::get().refungible_sponsor_transfer_timeout2487                            };2488    2489                            let mut sponsored = true;2490                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2491                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2492                                let limit_time = last_tx_block + limit.into();2493                                if block_number <= limit_time {2494                                    sponsored = false;2495                                }2496                            }2497                            if sponsored {2498                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2499                            }25002501                            sponsored2502                        }2503                        _ => {2504                            false2505                        },2506                    };2507                }25082509                if !sponsor_transfer {2510                    T::AccountId::default()2511                } else {2512                    <Collection<T>>::get(collection_id).sponsor2513                }2514            }25152516            _ => T::AccountId::default(),2517        };25182519        // Sponsor smart contracts2520        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {25212522            // On instantiation: set the contract owner2523            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {25242525                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2526                    &who,2527                    code_hash,2528                    salt,2529                );2530                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());25312532                T::AccountId::default()2533            },25342535            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2536            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {25372538                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());25392540                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2541                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2542                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2543                  2544                if !owned_contract && white_list_enabled {2545                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2546                        return Err(InvalidTransaction::Call.into());2547                    }2548                }25492550                let mut sponsor_transfer = false;2551                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2552                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2553                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2554                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2555                    let limit_time = last_tx_block + rate_limit;25562557                    if block_number >= limit_time {2558                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2559                        sponsor_transfer = true;2560                    }2561                } else {2562                    sponsor_transfer = false;2563                }2564               2565                2566                let mut sp = T::AccountId::default();2567                if sponsor_transfer {2568                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2569                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2570                            sp = called_contract;2571                        }2572                    }2573                }25742575                sp2576            },25772578            _ => sponsor,2579        };25802581        let mut who_pays_fee: T::AccountId = sponsor.clone();2582        if sponsor == T::AccountId::default() {2583            who_pays_fee = who.clone();2584        }25852586		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2587			.map(|i| (fee, i))2588    }2589}259025912592impl<T: Config + Send + Sync> SignedExtension2593    for ChargeTransactionPayment<T>2594where2595    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2596    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2597    T::AccountId: AsRef<[u8]>,2598    T::AccountId: UncheckedFrom<T::Hash>,2599{2600    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2601    type AccountId = T::AccountId;2602    type Call = T::Call;2603    type AdditionalSigned = ();2604    type Pre = (2605        // tip2606        BalanceOf<T>,2607        // who pays fee2608        Self::AccountId,2609		// imbalance resulting from withdrawing the fee2610		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2611    );2612    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2613        Ok(())2614    }26152616    fn validate(2617        &self,2618        who: &Self::AccountId,2619        call: &Self::Call,2620        info: &DispatchInfoOf<Self::Call>,2621        len: usize,2622    ) -> TransactionValidity {2623		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2624		Ok(ValidTransaction {2625			priority: Self::get_priority(len, info, fee),2626			..Default::default()2627		})2628    }26292630    fn pre_dispatch(2631        self,2632        who: &Self::AccountId,2633        call: &Self::Call,2634        info: &DispatchInfoOf<Self::Call>,2635        len: usize,2636    ) -> Result<Self::Pre, TransactionValidityError> {2637        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2638        Ok((self.0, who.clone(), imbalance))2639    }26402641    fn post_dispatch(2642        pre: Self::Pre,2643        info: &DispatchInfoOf<Self::Call>,2644        post_info: &PostDispatchInfoOf<Self::Call>,2645        len: usize,2646        _result: &DispatchResult,2647    ) -> Result<(), TransactionValidityError> {2648		let (tip, who, imbalance) = pre;2649		let actual_fee = transaction_payment::Module::<T>::compute_actual_fee(2650			len as u32,2651			info,2652			post_info,2653			tip,2654		);2655		<T as transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2656		Ok(())2657    }2658}26592660// #endregion