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

difftreelog

source

pallets/nft/src/lib.rs99.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,198            sponsor_transfer_timeout: 14400,199            owner_can_transfer: true,200            owner_can_destroy: true201        }202    }203}204205#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]206#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]207pub struct ChainLimits {208    pub collection_numbers_limit: u32,209    pub account_token_ownership_limit: u32,210    pub collections_admins_limit: u64,211    pub custom_data_limit: u32,212213    // Timeouts for item types in passed blocks214    pub nft_sponsor_transfer_timeout: u32,215    pub fungible_sponsor_transfer_timeout: u32,216    pub refungible_sponsor_transfer_timeout: u32,217218    // Schema limits219    pub offchain_schema_limit: u32,220    pub variable_on_chain_schema_limit: u32,221    pub const_on_chain_schema_limit: u32,222}223224pub trait WeightInfo {225	fn create_collection() -> Weight;226	fn destroy_collection() -> Weight;227	fn add_to_white_list() -> Weight;228	fn remove_from_white_list() -> Weight;229    fn set_public_access_mode() -> Weight;230    fn set_mint_permission() -> Weight;231    fn change_collection_owner() -> Weight;232    fn add_collection_admin() -> Weight;233    fn remove_collection_admin() -> Weight;234    fn set_collection_sponsor() -> Weight;235    fn confirm_sponsorship() -> Weight;236    fn remove_collection_sponsor() -> Weight;237    fn create_item(s: usize) -> Weight;238    fn burn_item() -> Weight;239    fn transfer() -> Weight;240    fn approve() -> Weight;241    fn transfer_from() -> Weight;242    fn set_offchain_schema() -> Weight;243    fn set_const_on_chain_schema() -> Weight;244    fn set_variable_on_chain_schema() -> Weight;245    fn set_variable_meta_data() -> Weight;246    fn enable_contract_sponsoring() -> Weight;247    fn set_schema_version() -> Weight;248    fn set_chain_limits() -> Weight;249    fn set_contract_sponsoring_rate_limit() -> Weight;250    fn toggle_contract_white_list() -> Weight;251    fn add_to_contract_white_list() -> Weight;252    fn remove_from_contract_white_list() -> Weight;253    fn set_collection_limits() -> Weight;254}255256#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]257#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]258pub struct CreateNftData {259    pub const_data: Vec<u8>,260    pub variable_data: Vec<u8>,261}262263#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]264#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]265pub struct CreateFungibleData {266    pub value: u128,267}268269#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]270#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]271pub struct CreateReFungibleData {272    pub const_data: Vec<u8>,273    pub variable_data: Vec<u8>,274    pub pieces: u128,275}276277#[derive(Encode, Decode, Debug, Clone, PartialEq)]278#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]279pub enum CreateItemData {280    NFT(CreateNftData),281    Fungible(CreateFungibleData),282    ReFungible(CreateReFungibleData),283}284285impl CreateItemData {286    pub fn len(&self) -> usize {287        let len = match self {288            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),289            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),290            _ => 0291        };292        293        return len;294    }295}296297impl From<CreateNftData> for CreateItemData {298    fn from(item: CreateNftData) -> Self {299        CreateItemData::NFT(item)300    }301}302303impl From<CreateReFungibleData> for CreateItemData {304    fn from(item: CreateReFungibleData) -> Self {305        CreateItemData::ReFungible(item)306    }307}308309impl From<CreateFungibleData> for CreateItemData {310    fn from(item: CreateFungibleData) -> Self {311        CreateItemData::Fungible(item)312    }313}314315316decl_error! {317	/// Error for non-fungible-token module.318	pub enum Error for Module<T: Config> {319        /// Total collections bound exceeded.320        TotalCollectionsLimitExceeded,321		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.322        CollectionDecimalPointLimitExceeded, 323        /// Collection name can not be longer than 63 char.324        CollectionNameLimitExceeded, 325        /// Collection description can not be longer than 255 char.326        CollectionDescriptionLimitExceeded, 327        /// Token prefix can not be longer than 15 char.328        CollectionTokenPrefixLimitExceeded,329        /// This collection does not exist.330        CollectionNotFound,331        /// Item not exists.332        TokenNotFound,333        /// Admin not found334        AdminNotFound,335        /// Arithmetic calculation overflow.336        NumOverflow,       337        /// Account already has admin role.338        AlreadyAdmin,  339        /// You do not own this collection.340        NoPermission,341        /// This address is not set as sponsor, use setCollectionSponsor first.342        ConfirmUnsetSponsorFail,343        /// Collection is not in mint mode.344        PublicMintingNotAllowed,345        /// Sender parameter and item owner must be equal.346        MustBeTokenOwner,347        /// Item balance not enough.348        TokenValueTooLow,349        /// Size of item is too large.350        NftSizeLimitExceeded,351        /// No approve found352        ApproveNotFound,353        /// Requested value more than approved.354        TokenValueNotEnough,355        /// Only approved addresses can call this method.356        ApproveRequired,357        /// Address is not in white list.358        AddresNotInWhiteList,359        /// Number of collection admins bound exceeded.360        CollectionAdminsLimitExceeded,361        /// Owned tokens by a single address bound exceeded.362        AddressOwnershipLimitExceeded,363        /// Length of items properties must be greater than 0.364        EmptyArgument,365        /// const_data exceeded data limit.366        TokenConstDataLimitExceeded,367        /// variable_data exceeded data limit.368        TokenVariableDataLimitExceeded,369        /// Not NFT item data used to mint in NFT collection.370        NotNftDataUsedToMintNftCollectionToken,371        /// Not Fungible item data used to mint in Fungible collection.372        NotFungibleDataUsedToMintFungibleCollectionToken,373        /// Not Re Fungible item data used to mint in Re Fungible collection.374        NotReFungibleDataUsedToMintReFungibleCollectionToken,375        /// Unexpected collection type.376        UnexpectedCollectionType,377        /// Can't store metadata in fungible tokens.378        CantStoreMetadataInFungibleTokens,379        /// Collection token limit exceeded380        CollectionTokenLimitExceeded,381        /// Account token limit exceeded per collection382        AccountTokenLimitExceeded,383        /// Collection limit bounds per collection exceeded384        CollectionLimitBoundsExceeded,385        /// Tried to enable permissions which are only permitted to be disabled386        OwnerPermissionsCantBeReverted,387        /// Schema data size limit bound exceeded388        SchemaDataLimitExceeded,389        /// Maximum refungibility exceeded390        WrongRefungiblePieces391	}392}393394pub trait Config: system::Config + Sized + transaction_payment::Config + pallet_contracts::Config {395    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;396397    /// Weight information for extrinsics in this pallet.398	type WeightInfo: WeightInfo;399}400401#[cfg(feature = "runtime-benchmarks")]402mod benchmarking;403404// #endregion405406decl_storage! {407    trait Store for Module<T: Config> as Nft {408409        // Private members410        NextCollectionID: CollectionId;411        CreatedCollectionCount: u32;412        ChainVersion: u64;413        ItemListIndex: map hasher(identity) CollectionId => TokenId;414415        // Chain limits struct416        pub ChainLimit get(fn chain_limit) config(): ChainLimits;417418        // Bound counters419        CollectionCount: u32;420        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;421422        // Basic collections423        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;424        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;425        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;426427        /// Balance owner per collection map428        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;429430        /// second parameter: item id + owner account id + spender account id431        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;432433        /// Item collections434        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;435        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;436        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;437438        /// Index list439        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;440441        /// Tokens transfer baskets442        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;443        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;444        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;445        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;446447        // Contract Sponsorship and Ownership448        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;449        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;450        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;451        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;452        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 453        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 454    }455    add_extra_genesis {456        build(|config: &GenesisConfig<T>| {457            // Modification of storage458            for (_num, _c) in &config.collection {459                <Module<T>>::init_collection(_c);460            }461462            for (_num, _c, _i) in &config.nft_item_id {463                <Module<T>>::init_nft_token(*_c, _i);464            }465466            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {467                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);468            }469470            for (_num, _c, _i) in &config.refungible_item_id {471                <Module<T>>::init_refungible_token(*_c, _i);472            }473        })474    }475}476477decl_event!(478    pub enum Event<T>479    where480        AccountId = <T as system::Config>::AccountId,481    {482        /// New collection was created483        /// 484        /// # Arguments485        /// 486        /// * collection_id: Globally unique identifier of newly created collection.487        /// 488        /// * mode: [CollectionMode] converted into u8.489        /// 490        /// * account_id: Collection owner.491        Created(CollectionId, u8, AccountId),492493        /// New item was created.494        /// 495        /// # Arguments496        /// 497        /// * collection_id: Id of the collection where item was created.498        /// 499        /// * item_id: Id of an item. Unique within the collection.500        ///501        /// * recipient: Owner of newly created item 502        ItemCreated(CollectionId, TokenId, AccountId),503504        /// Collection item was burned.505        /// 506        /// # Arguments507        /// 508        /// collection_id.509        /// 510        /// item_id: Identifier of burned NFT.511        ItemDestroyed(CollectionId, TokenId),512513        /// Item was transferred514        ///515        /// * collection_id: Id of collection to which item is belong516        ///517        /// * item_id: Id of an item518        ///519        /// * sender: Original owner of item520        ///521        /// * recipient: New owner of item522        ///523        /// * amount: Always 1 for NFT524        Transfer(CollectionId, TokenId, AccountId, AccountId, u128),525    }526);527528decl_module! {529    pub struct Module<T: Config> for enum Call 530    where 531        origin: T::Origin532    {533        fn deposit_event() = default;534        type Error = Error<T>;535536        fn on_initialize(now: T::BlockNumber) -> Weight {537538            if ChainVersion::get() < 2539            {540                let value = NextCollectionID::get();541                CreatedCollectionCount::put(value);542                ChainVersion::put(2);543            }544545            0546        }547548        /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.549        /// 550        /// # Permissions551        /// 552        /// * Anyone.553        /// 554        /// # Arguments555        /// 556        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.557        /// 558        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.559        /// 560        /// * token_prefix: UTF-8 string with token prefix.561        /// 562        /// * mode: [CollectionMode] collection type and type dependent data.563        // returns collection ID564        #[weight = <T as Config>::WeightInfo::create_collection()]565        pub fn create_collection(origin,566                                 collection_name: Vec<u16>,567                                 collection_description: Vec<u16>,568                                 token_prefix: Vec<u8>,569                                 mode: CollectionMode) -> DispatchResult {570571            // Anyone can create a collection572            let who = ensure_signed(origin)?;573574            let decimal_points = match mode {575                CollectionMode::Fungible(points) => points,576                _ => 0577            };578579            let chain_limit = ChainLimit::get();580581            // bound Total number of collections582            ensure!(CollectionCount::get() < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);583584            // check params585            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);586            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);587            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);588            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);589590            // Generate next collection ID591            let next_id = CreatedCollectionCount::get()592                .checked_add(1)593                .ok_or(Error::<T>::NumOverflow)?;594595            // bound counter596            let total = CollectionCount::get()597                .checked_add(1)598                .ok_or(Error::<T>::NumOverflow)?;599600            CreatedCollectionCount::put(next_id);601            CollectionCount::put(total);602603            let limits = CollectionLimits {604                sponsored_data_size: chain_limit.custom_data_limit,605                ..Default::default()606            };607608            // Create new collection609            let new_collection = CollectionType {610                owner: who.clone(),611                name: collection_name,612                mode: mode.clone(),613                mint_mode: false,614                access: AccessMode::Normal,615                description: collection_description,616                decimal_points: decimal_points,617                token_prefix: token_prefix,618                offchain_schema: Vec::new(),619                schema_version: SchemaVersion::ImageURL,620                sponsor: T::AccountId::default(),621                sponsor_confirmed: false,622                variable_on_chain_schema: Vec::new(),623                const_on_chain_schema: Vec::new(),624                limits,625            };626627            // Add new collection to map628            <Collection<T>>::insert(next_id, new_collection);629630            // call event631            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));632633            Ok(())634        }635636        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.637        /// 638        /// # Permissions639        /// 640        /// * Collection Owner.641        /// 642        /// # Arguments643        /// 644        /// * collection_id: collection to destroy.645        #[weight = <T as Config>::WeightInfo::destroy_collection()]646        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {647648            let sender = ensure_signed(origin)?;649            Self::check_owner_permissions(collection_id, sender)?;650651            let target_collection = <Collection<T>>::get(collection_id);652            if !target_collection.limits.owner_can_destroy {653                fail!(Error::<T>::NoPermission);654            }655656            <AddressTokens<T>>::remove_prefix(collection_id);657            <Allowances<T>>::remove_prefix(collection_id);658            <Balance<T>>::remove_prefix(collection_id);659            <ItemListIndex>::remove(collection_id);660            <AdminList<T>>::remove(collection_id);661            <Collection<T>>::remove(collection_id);662            <WhiteList<T>>::remove_prefix(collection_id);663664            <NftItemList<T>>::remove_prefix(collection_id);665            <FungibleItemList<T>>::remove_prefix(collection_id);666            <ReFungibleItemList<T>>::remove_prefix(collection_id);667668            <NftTransferBasket<T>>::remove_prefix(collection_id);669            <FungibleTransferBasket<T>>::remove_prefix(collection_id);670            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);671672            if CollectionCount::get() > 0673            {674                // bound couter675                let total = CollectionCount::get()676                    .checked_sub(1)677                    .ok_or(Error::<T>::NumOverflow)?;678679                CollectionCount::put(total);680            }681682            Ok(())683        }684685        /// Add an address to white list.686        /// 687        /// # Permissions688        /// 689        /// * Collection Owner690        /// * Collection Admin691        /// 692        /// # Arguments693        /// 694        /// * collection_id.695        /// 696        /// * address.697        #[weight = <T as Config>::WeightInfo::add_to_white_list()]698        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{699700            let sender = ensure_signed(origin)?;701            Self::check_owner_or_admin_permissions(collection_id, sender)?;702703            <WhiteList<T>>::insert(collection_id, address, true);704            705            Ok(())706        }707708        /// Remove an address from white list.709        /// 710        /// # Permissions711        /// 712        /// * Collection Owner713        /// * Collection Admin714        /// 715        /// # Arguments716        /// 717        /// * collection_id.718        /// 719        /// * address.720        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]721        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{722723            let sender = ensure_signed(origin)?;724            Self::check_owner_or_admin_permissions(collection_id, sender)?;725726            <WhiteList<T>>::remove(collection_id, address);727728            Ok(())729        }730731        /// Toggle between normal and white list access for the methods with access for `Anyone`.732        /// 733        /// # Permissions734        /// 735        /// * Collection Owner.736        /// 737        /// # Arguments738        /// 739        /// * collection_id.740        /// 741        /// * mode: [AccessMode]742        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]743        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult744        {745            let sender = ensure_signed(origin)?;746747            Self::check_owner_permissions(collection_id, sender)?;748            let mut target_collection = <Collection<T>>::get(collection_id);749            target_collection.access = mode;750            <Collection<T>>::insert(collection_id, target_collection);751752            Ok(())753        }754755        /// Allows Anyone to create tokens if:756        /// * White List is enabled, and757        /// * Address is added to white list, and758        /// * This method was called with True parameter759        /// 760        /// # Permissions761        /// * Collection Owner762        ///763        /// # Arguments764        /// 765        /// * collection_id.766        /// 767        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.768        #[weight = <T as Config>::WeightInfo::set_mint_permission()]769        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult770        {771            let sender = ensure_signed(origin)?;772773            Self::check_owner_permissions(collection_id, sender)?;774            let mut target_collection = <Collection<T>>::get(collection_id);775            target_collection.mint_mode = mint_permission;776            <Collection<T>>::insert(collection_id, target_collection);777778            Ok(())779        }780781        /// Change the owner of the collection.782        /// 783        /// # Permissions784        /// 785        /// * Collection Owner.786        /// 787        /// # Arguments788        /// 789        /// * collection_id.790        /// 791        /// * new_owner.792        #[weight = <T as Config>::WeightInfo::change_collection_owner()]793        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {794795            let sender = ensure_signed(origin)?;796            Self::check_owner_permissions(collection_id, sender)?;797            let mut target_collection = <Collection<T>>::get(collection_id);798            target_collection.owner = new_owner;799            <Collection<T>>::insert(collection_id, target_collection);800801            Ok(())802        }803804        /// Adds an admin of the Collection.805        /// 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. 806        /// 807        /// # Permissions808        /// 809        /// * Collection Owner.810        /// * Collection Admin.811        /// 812        /// # Arguments813        /// 814        /// * collection_id: ID of the Collection to add admin for.815        /// 816        /// * new_admin_id: Address of new admin to add.817        #[weight = <T as Config>::WeightInfo::add_collection_admin()]818        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {819820            let sender = ensure_signed(origin)?;821            Self::check_owner_or_admin_permissions(collection_id, sender)?;822            let mut admin_arr: Vec<T::AccountId> = Vec::new();823824            if <AdminList<T>>::contains_key(collection_id)825            {826                admin_arr = <AdminList<T>>::get(collection_id);827                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);828            }829830            // Number of collection admins831            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);832833            admin_arr.push(new_admin_id);834            <AdminList<T>>::insert(collection_id, admin_arr);835836            Ok(())837        }838839        /// 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.840        ///841        /// # Permissions842        /// 843        /// * Collection Owner.844        /// * Collection Admin.845        /// 846        /// # Arguments847        /// 848        /// * collection_id: ID of the Collection to remove admin for.849        /// 850        /// * account_id: Address of admin to remove.851        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]852        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {853854            let sender = ensure_signed(origin)?;855            Self::check_owner_or_admin_permissions(collection_id, sender)?;856            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);857858            let mut admin_arr = <AdminList<T>>::get(collection_id);859            admin_arr.retain(|i| *i != account_id);860            <AdminList<T>>::insert(collection_id, admin_arr);861862            Ok(())863        }864865        /// # Permissions866        /// 867        /// * Collection Owner868        /// 869        /// # Arguments870        /// 871        /// * collection_id.872        /// 873        /// * new_sponsor.874        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]875        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {876877            let sender = ensure_signed(origin)?;878            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);879880            let mut target_collection = <Collection<T>>::get(collection_id);881            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);882883            target_collection.sponsor = new_sponsor;884            target_collection.sponsor_confirmed = false;885            <Collection<T>>::insert(collection_id, target_collection);886887            Ok(())888        }889890        /// # Permissions891        /// 892        /// * Sponsor.893        /// 894        /// # Arguments895        /// 896        /// * collection_id.897        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]898        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {899900            let sender = ensure_signed(origin)?;901            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);902903            let mut target_collection = <Collection<T>>::get(collection_id);904            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);905906            target_collection.sponsor_confirmed = true;907            <Collection<T>>::insert(collection_id, target_collection);908909            Ok(())910        }911912        /// Switch back to pay-per-own-transaction model.913        ///914        /// # Permissions915        ///916        /// * Collection owner.917        /// 918        /// # Arguments919        /// 920        /// * collection_id.921        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]922        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {923924            let sender = ensure_signed(origin)?;925            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);926927            let mut target_collection = <Collection<T>>::get(collection_id);928            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);929930            target_collection.sponsor = T::AccountId::default();931            target_collection.sponsor_confirmed = false;932            <Collection<T>>::insert(collection_id, target_collection);933934            Ok(())935        }936937        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.938        /// 939        /// # Permissions940        /// 941        /// * Collection Owner.942        /// * Collection Admin.943        /// * Anyone if944        ///     * White List is enabled, and945        ///     * Address is added to white list, and946        ///     * MintPermission is enabled (see SetMintPermission method)947        /// 948        /// # Arguments949        /// 950        /// * collection_id: ID of the collection.951        /// 952        /// * owner: Address, initial owner of the NFT.953        ///954        /// * data: Token data to store on chain.955        // #[weight =956        // (130_000_000 as Weight)957        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))958        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))959        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]960961        #[weight = <T as Config>::WeightInfo::create_item(data.len())]962        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {963964            let sender = ensure_signed(origin)?;965966            Self::collection_exists(collection_id)?;967968            let target_collection = <Collection<T>>::get(collection_id);969970            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner, 1)?;971            Self::validate_create_item_args(&target_collection, &data)?;972            Self::create_item_no_validation(collection_id, owner, data)?;973974            Ok(())975        }976977        /// This method creates multiple instances of NFT Collection created with CreateCollection method.978        /// 979        /// # Permissions980        /// 981        /// * Collection Owner.982        /// * Collection Admin.983        /// * Anyone if984        ///     * White List is enabled, and985        ///     * Address is added to white list, and986        ///     * MintPermission is enabled (see SetMintPermission method)987        /// 988        /// # Arguments989        /// 990        /// * collection_id: ID of the collection.991        /// 992        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].993        /// 994        /// * owner: Address, initial owner of the NFT.995        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()996                               .map(|data| { data.len() })997                               .sum())]998        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {9991000            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1001            let sender = ensure_signed(origin)?;10021003            Self::collection_exists(collection_id)?;1004            let target_collection = <Collection<T>>::get(collection_id);10051006            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner, items_data.len() as u32)?;10071008            for data in &items_data {1009                Self::validate_create_item_args(&target_collection, data)?;1010            }1011            for data in &items_data {1012                Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?;1013            }10141015            Ok(())1016        }10171018        /// Destroys a concrete instance of NFT.1019        /// 1020        /// # Permissions1021        /// 1022        /// * Collection Owner.1023        /// * Collection Admin.1024        /// * Current NFT Owner.1025        /// 1026        /// # Arguments1027        /// 1028        /// * collection_id: ID of the collection.1029        /// 1030        /// * item_id: ID of NFT to burn.1031        #[weight = <T as Config>::WeightInfo::burn_item()]1032        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10331034            let sender = ensure_signed(origin)?;1035            Self::collection_exists(collection_id)?;10361037            // Transfer permissions check1038            let target_collection = <Collection<T>>::get(collection_id);1039            ensure!(1040                Self::is_item_owner(sender.clone(), collection_id, item_id) ||1041                (1042                    target_collection.limits.owner_can_transfer &&1043                    Self::is_owner_or_admin_permissions(collection_id, sender.clone())1044                ),1045                Error::<T>::NoPermission1046            );10471048            if target_collection.access == AccessMode::WhiteList {1049                Self::check_white_list(collection_id, &sender)?;1050            }10511052            match target_collection.mode1053            {1054                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1055                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1056                CollectionMode::ReFungible  => Self::burn_refungible_item(collection_id, item_id, &sender)?,1057                _ => ()1058            };10591060            // call event1061            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10621063            Ok(())1064        }10651066        /// Change ownership of the token.1067        /// 1068        /// # Permissions1069        /// 1070        /// * Collection Owner1071        /// * Collection Admin1072        /// * Current NFT owner1073        ///1074        /// # Arguments1075        /// 1076        /// * recipient: Address of token recipient.1077        /// 1078        /// * collection_id.1079        /// 1080        /// * item_id: ID of the item1081        ///     * Non-Fungible Mode: Required.1082        ///     * Fungible Mode: Ignored.1083        ///     * Re-Fungible Mode: Required.1084        /// 1085        /// * value: Amount to transfer.1086        ///     * Non-Fungible Mode: Ignored1087        ///     * Fungible Mode: Must specify transferred amount1088        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1089        #[weight = <T as Config>::WeightInfo::transfer()]1090        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1091            let sender = ensure_signed(origin)?;1092            Self::transfer_internal(sender, recipient, collection_id, item_id, value)1093        }10941095        /// Set, change, or remove approved address to transfer the ownership of the NFT.1096        /// 1097        /// # Permissions1098        /// 1099        /// * Collection Owner1100        /// * Collection Admin1101        /// * Current NFT owner1102        /// 1103        /// # Arguments1104        /// 1105        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1106        /// 1107        /// * collection_id.1108        /// 1109        /// * item_id: ID of the item.1110        #[weight = <T as Config>::WeightInfo::approve()]1111        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {11121113            let sender = ensure_signed(origin)?;11141115            Self::collection_exists(collection_id)?;1116            Self::token_exists(collection_id, item_id, &sender)?;11171118            // Transfer permissions check1119            let target_collection = <Collection<T>>::get(collection_id);1120            let allowance_limit = if (1121                target_collection.limits.owner_can_transfer &&1122                Self::is_owner_or_admin_permissions(1123                    collection_id,1124                    sender.clone(),1125                )1126            ) {1127                None1128            } else if let Some(amount) = Self::owned_amount(1129                sender.clone(),1130                collection_id,1131                item_id,1132            ) {1133                Some(amount)1134            } else {1135                fail!(Error::<T>::NoPermission);1136            };11371138            if target_collection.access == AccessMode::WhiteList {1139                Self::check_white_list(collection_id, &sender)?;1140                Self::check_white_list(collection_id, &spender)?;1141            }11421143            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1144            let mut allowance: u128 = amount;1145            if allowance_exists {1146                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1147            }1148            if let Some(limit) = allowance_limit {1149                ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1150            }1151            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11521153            Ok(())1154        }1155        1156        /// 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.1157        /// 1158        /// # Permissions1159        /// * Collection Owner1160        /// * Collection Admin1161        /// * Current NFT owner1162        /// * Address approved by current NFT owner1163        /// 1164        /// # Arguments1165        /// 1166        /// * from: Address that owns token.1167        /// 1168        /// * recipient: Address of token recipient.1169        /// 1170        /// * collection_id.1171        /// 1172        /// * item_id: ID of the item.1173        /// 1174        /// * value: Amount to transfer.1175        #[weight = <T as Config>::WeightInfo::transfer_from()]1176        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11771178            let sender = ensure_signed(origin)?;1179            let mut appoved_transfer = false;11801181            // Check approval1182            let mut approval: u128 = 0;1183            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1184                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1185                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1186                appoved_transfer = true;1187            }11881189            let target_collection = <Collection<T>>::get(collection_id);11901191            // Limits check1192            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11931194            // Transfer permissions check         1195            ensure!(1196                appoved_transfer || 1197                (1198                    target_collection.limits.owner_can_transfer &&1199                    Self::is_owner_or_admin_permissions(collection_id, sender.clone())1200                ),1201                Error::<T>::NoPermission1202            );12031204            if target_collection.access == AccessMode::WhiteList {1205                Self::check_white_list(collection_id, &sender)?;1206                Self::check_white_list(collection_id, &recipient)?;1207            }12081209            // Reduce approval by transferred amount or remove if remaining approval drops to 01210            if approval.checked_sub(value).unwrap_or(0) > 0 {1211                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1212            }1213            else {1214                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1215            }12161217            match target_collection.mode1218            {1219                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1220                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1221                CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1222                _ => ()1223            };12241225            Ok(())1226        }12271228        // #[weight = 0]1229        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {12301231        //     // let no_perm_mes = "You do not have permissions to modify this collection";1232        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1233        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1234        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);12351236        //     // // on_nft_received  call12371238        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;12391240        //     Ok(())1241        // }12421243        /// Set off-chain data schema.1244        /// 1245        /// # Permissions1246        /// 1247        /// * Collection Owner1248        /// * Collection Admin1249        /// 1250        /// # Arguments1251        /// 1252        /// * collection_id.1253        /// 1254        /// * schema: String representing the offchain data schema.1255        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1256        pub fn set_variable_meta_data (1257            origin,1258            collection_id: CollectionId,1259            item_id: TokenId,1260            data: Vec<u8>1261        ) -> DispatchResult {1262            let sender = ensure_signed(origin)?;1263            1264            Self::collection_exists(collection_id)?;1265            Self::token_exists(collection_id, item_id, &sender)?;12661267            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12681269            // Modify permissions check1270            let target_collection = <Collection<T>>::get(collection_id);1271            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1272                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1273                Error::<T>::NoPermission);12741275            match target_collection.mode1276            {1277                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1278                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1279                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1280                _ => fail!(Error::<T>::UnexpectedCollectionType)1281            };12821283            Ok(())1284        }1285 1286        /// Set schema standard1287        /// ImageURL1288        /// Unique1289        /// 1290        /// # Permissions1291        /// 1292        /// * Collection Owner1293        /// * Collection Admin1294        /// 1295        /// # Arguments1296        /// 1297        /// * collection_id.1298        /// 1299        /// * schema: SchemaVersion: enum1300        #[weight = <T as Config>::WeightInfo::set_schema_version()]1301        pub fn set_schema_version(1302            origin,1303            collection_id: CollectionId,1304            version: SchemaVersion1305        ) -> DispatchResult {1306            let sender = ensure_signed(origin)?;1307            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1308            let mut target_collection = <Collection<T>>::get(collection_id);1309            target_collection.schema_version = version;1310            <Collection<T>>::insert(collection_id, target_collection);13111312            Ok(())1313        }13141315        /// Set off-chain data schema.1316        /// 1317        /// # Permissions1318        /// 1319        /// * Collection Owner1320        /// * Collection Admin1321        /// 1322        /// # Arguments1323        /// 1324        /// * collection_id.1325        /// 1326        /// * schema: String representing the offchain data schema.1327        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1328        pub fn set_offchain_schema(1329            origin,1330            collection_id: CollectionId,1331            schema: Vec<u8>1332        ) -> DispatchResult {1333            let sender = ensure_signed(origin)?;1334            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13351336            // check schema limit1337            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");13381339            let mut target_collection = <Collection<T>>::get(collection_id);1340            target_collection.offchain_schema = schema;1341            <Collection<T>>::insert(collection_id, target_collection);13421343            Ok(())1344        }13451346        /// Set const on-chain data schema.1347        /// 1348        /// # Permissions1349        /// 1350        /// * Collection Owner1351        /// * Collection Admin1352        /// 1353        /// # Arguments1354        /// 1355        /// * collection_id.1356        /// 1357        /// * schema: String representing the const on-chain data schema.1358        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1359        pub fn set_const_on_chain_schema (1360            origin,1361            collection_id: CollectionId,1362            schema: Vec<u8>1363        ) -> DispatchResult {1364            let sender = ensure_signed(origin)?;1365            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13661367            // check schema limit1368            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");13691370            let mut target_collection = <Collection<T>>::get(collection_id);1371            target_collection.const_on_chain_schema = schema;1372            <Collection<T>>::insert(collection_id, target_collection);13731374            Ok(())1375        }13761377        /// Set variable on-chain data schema.1378        /// 1379        /// # Permissions1380        /// 1381        /// * Collection Owner1382        /// * Collection Admin1383        /// 1384        /// # Arguments1385        /// 1386        /// * collection_id.1387        /// 1388        /// * schema: String representing the variable on-chain data schema.1389        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1390        pub fn set_variable_on_chain_schema (1391            origin,1392            collection_id: CollectionId,1393            schema: Vec<u8>1394        ) -> DispatchResult {1395            let sender = ensure_signed(origin)?;1396            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13971398            // check schema limit1399            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14001401            let mut target_collection = <Collection<T>>::get(collection_id);1402            target_collection.variable_on_chain_schema = schema;1403            <Collection<T>>::insert(collection_id, target_collection);14041405            Ok(())1406        }14071408        // Sudo permissions function1409        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1410        pub fn set_chain_limits(1411            origin,1412            limits: ChainLimits1413        ) -> DispatchResult {14141415            #[cfg(not(feature = "runtime-benchmarks"))]1416            ensure_root(origin)?;14171418            <ChainLimit>::put(limits);1419            Ok(())1420        }14211422        /// Enable smart contract self-sponsoring.1423        /// 1424        /// # Permissions1425        /// 1426        /// * Contract Owner1427        /// 1428        /// # Arguments1429        /// 1430        /// * contract address1431        /// * enable flag1432        /// 1433        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1434        pub fn enable_contract_sponsoring(1435            origin,1436            contract_address: T::AccountId,1437            enable: bool1438        ) -> DispatchResult {14391440            let sender = ensure_signed(origin)?;14411442            #[cfg(feature = "runtime-benchmarks")]1443            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14441445            Self::ensure_contract_owned(sender, &contract_address)?;14461447            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1448            Ok(())1449        }14501451        /// Set the rate limit for contract sponsoring to specified number of blocks.1452        /// 1453        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1454        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1455        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1456        /// from contract endowment if there are at least B blocks between such transactions. 1457        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1458        /// 1459        /// # Permissions1460        /// 1461        /// * Contract Owner1462        /// 1463        /// # Arguments1464        /// 1465        /// -`contract_address`: Address of the contract to sponsor1466        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1467        /// 1468        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1469        pub fn set_contract_sponsoring_rate_limit(1470            origin,1471            contract_address: T::AccountId,1472            rate_limit: T::BlockNumber1473        ) -> DispatchResult {1474            let sender = ensure_signed(origin)?;14751476            #[cfg(feature = "runtime-benchmarks")]1477            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14781479            Self::ensure_contract_owned(sender, &contract_address)?;1480            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1481            Ok(())1482        }14831484        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1485        /// 1486        /// # Permissions1487        /// 1488        /// * Address that deployed smart contract.1489        /// 1490        /// # Arguments1491        /// 1492        /// -`contract_address`: Address of the contract.1493        /// 1494        /// - `enable`: .  1495        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1496        pub fn toggle_contract_white_list(1497            origin,1498            contract_address: T::AccountId,1499            enable: bool1500        ) -> DispatchResult {1501            let sender = ensure_signed(origin)?;15021503            #[cfg(feature = "runtime-benchmarks")]1504            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15051506            Self::ensure_contract_owned(sender, &contract_address)?;1507            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1508            Ok(())1509        }1510        1511        /// Add an address to smart contract white list.1512        /// 1513        /// # Permissions1514        /// 1515        /// * Address that deployed smart contract.1516        /// 1517        /// # Arguments1518        /// 1519        /// -`contract_address`: Address of the contract.1520        ///1521        /// -`account_address`: Address to add.1522        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1523        pub fn add_to_contract_white_list(1524            origin,1525            contract_address: T::AccountId,1526            account_address: T::AccountId1527        ) -> DispatchResult {1528            let sender = ensure_signed(origin)?;15291530            #[cfg(feature = "runtime-benchmarks")]1531            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1532            1533            Self::ensure_contract_owned(sender, &contract_address)?;      1534            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1535            Ok(())1536        }15371538        /// Remove an address from smart contract white list.1539        /// 1540        /// # Permissions1541        /// 1542        /// * Address that deployed smart contract.1543        /// 1544        /// # Arguments1545        /// 1546        /// -`contract_address`: Address of the contract.1547        ///1548        /// -`account_address`: Address to remove.1549        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1550        pub fn remove_from_contract_white_list(1551            origin,1552            contract_address: T::AccountId,1553            account_address: T::AccountId1554        ) -> DispatchResult {1555            let sender = ensure_signed(origin)?;15561557            #[cfg(feature = "runtime-benchmarks")]1558            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15591560            Self::ensure_contract_owned(sender, &contract_address)?;1561            <ContractWhiteList<T>>::remove(contract_address, account_address);1562            Ok(())1563        }15641565        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1566        pub fn set_collection_limits(1567            origin,1568            collection_id: u32,1569            new_limits: CollectionLimits,1570        ) -> DispatchResult {1571            let sender = ensure_signed(origin)?;1572            Self::check_owner_permissions(collection_id, sender.clone())?;1573            let mut target_collection = <Collection<T>>::get(collection_id);1574            let old_limits = target_collection.limits;1575            let chain_limits = ChainLimit::get();15761577            // collection bounds1578            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1579                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1580                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1581                Error::<T>::CollectionLimitBoundsExceeded);15821583            // token_limit   check  prev1584            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1585            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);15861587            ensure!(1588                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1589                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1590                Error::<T>::OwnerPermissionsCantBeReverted,1591            );15921593            target_collection.limits = new_limits;1594            <Collection<T>>::insert(collection_id, target_collection);15951596            Ok(())1597        } 1598    }1599}16001601impl<T: Config> Module<T> {16021603    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {16041605        let target_collection = <Collection<T>>::get(collection_id);16061607        // Limits check1608        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;16091610        // Transfer permissions check1611        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1612            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1613            Error::<T>::NoPermission);16141615        if target_collection.access == AccessMode::WhiteList {1616            Self::check_white_list(collection_id, &sender)?;1617            Self::check_white_list(collection_id, &recipient)?;1618        }16191620        match target_collection.mode1621        {1622            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient.clone())?,1623            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1624            CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient.clone())?,1625            _ => ()1626        };16271628        Self::deposit_event(RawEvent::Transfer(collection_id, item_id, sender, recipient, value));16291630        Ok(())1631    }163216331634    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {16351636        // check token limit and account token limit1637        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1638        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1639        1640        Ok(())1641    }16421643    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId, amount: u32) -> DispatchResult {16441645        // check token limit and account token limit1646        let total_items: u32 = ItemListIndex::get(collection_id)1647            .checked_add(amount)1648            .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1649        let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner).len() as u32)1650            .checked_add(amount)1651            .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1652        ensure!(collection.limits.token_limit >= total_items,  Error::<T>::CollectionTokenLimitExceeded);1653        ensure!(collection.limits.account_token_ownership_limit >= account_items,  Error::<T>::AccountTokenLimitExceeded);16541655        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1656            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1657            Self::check_white_list(collection_id, owner)?;1658            Self::check_white_list(collection_id, sender)?;1659        }16601661        Ok(())1662    }16631664    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1665        match target_collection.mode1666        {1667            CollectionMode::NFT => {1668                if let CreateItemData::NFT(data) = data {1669                    // check sizes1670                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1671                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1672                } else {1673                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1674                }1675            },1676            CollectionMode::Fungible(_) => {1677                if let CreateItemData::Fungible(_) = data {1678                } else {1679                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1680                }1681            },1682            CollectionMode::ReFungible => {1683                if let CreateItemData::ReFungible(data) = data {16841685                    // check sizes1686                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1687                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16881689                    // Check refungibility limits1690                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1691                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1692                } else {1693                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1694                }1695            },1696            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1697        };16981699        Ok(())1700    }17011702    fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1703        match data1704        {1705            CreateItemData::NFT(data) => {1706                let item = NftItemType {1707                    owner: owner.clone(),1708                    const_data: data.const_data,1709                    variable_data: data.variable_data1710                };17111712                Self::add_nft_item(collection_id, item)?;1713            },1714            CreateItemData::Fungible(data) => {1715                Self::add_fungible_item(collection_id, &owner, data.value)?;1716            },1717            CreateItemData::ReFungible(data) => {1718                let mut owner_list = Vec::new();1719                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});17201721                let item = ReFungibleItemType {1722                    owner: owner_list,1723                    const_data: data.const_data,1724                    variable_data: data.variable_data1725                };17261727                Self::add_refungible_item(collection_id, item)?;1728            }1729        };17301731        // call event1732        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id), owner));17331734        Ok(())1735    }17361737    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {17381739        // Does new owner already have an account?1740        let mut balance: u128 = 0;1741        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1742            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1743        } 17441745        // Mint 1746        let item = FungibleItemType {1747            value: balance + value1748        };1749        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);17501751        // Update balance1752        let new_balance = <Balance<T>>::get(collection_id, owner)1753            .checked_add(value)1754            .ok_or(Error::<T>::NumOverflow)?;1755        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17561757        Ok(())1758    }17591760    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1761        let current_index = <ItemListIndex>::get(collection_id)1762            .checked_add(1)1763            .ok_or(Error::<T>::NumOverflow)?;1764        let itemcopy = item.clone();17651766        let value = item.owner.first().unwrap().fraction;1767        let owner = item.owner.first().unwrap().owner.clone();17681769        Self::add_token_index(collection_id, current_index, &owner)?;17701771        <ItemListIndex>::insert(collection_id, current_index);1772        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17731774        // Update balance1775        let new_balance = <Balance<T>>::get(collection_id, &owner)1776            .checked_add(value)1777            .ok_or(Error::<T>::NumOverflow)?;1778        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17791780        Ok(())1781    }17821783    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1784        let current_index = <ItemListIndex>::get(collection_id)1785            .checked_add(1)1786            .ok_or(Error::<T>::NumOverflow)?;17871788        let item_owner = item.owner.clone();1789        Self::add_token_index(collection_id, current_index, &item.owner)?;17901791        <ItemListIndex>::insert(collection_id, current_index);1792        <NftItemList<T>>::insert(collection_id, current_index, item);17931794        // Update balance1795        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1796            .checked_add(1)1797            .ok_or(Error::<T>::NumOverflow)?;1798        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17991800        Ok(())1801    }18021803    fn burn_refungible_item(1804        collection_id: CollectionId,1805        item_id: TokenId,1806        owner: &T::AccountId,1807    ) -> DispatchResult {1808        ensure!(1809            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1810            Error::<T>::TokenNotFound1811        );1812        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1813        let rft_balance = token1814            .owner1815            .iter()1816            .filter(|&i| i.owner == *owner)1817            .next()1818            .unwrap();1819        Self::remove_token_index(collection_id, item_id, owner)?;18201821        // update balance1822        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1823            .checked_sub(rft_balance.fraction)1824            .ok_or(Error::<T>::NumOverflow)?;1825        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);18261827        // Re-create owners list with sender removed1828        let index = token1829            .owner1830            .iter()1831            .position(|i| i.owner == *owner)1832            .unwrap();1833        token.owner.remove(index);1834        let owner_count = token.owner.len();18351836        // Burn the token completely if this was the last (only) owner1837        if owner_count == 0 {1838            <ReFungibleItemList<T>>::remove(collection_id, item_id);1839        }1840        else {1841            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1842        }18431844        Ok(())1845    }18461847    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1848        ensure!(1849            <NftItemList<T>>::contains_key(collection_id, item_id),1850            Error::<T>::TokenNotFound1851        );1852        let item = <NftItemList<T>>::get(collection_id, item_id);1853        Self::remove_token_index(collection_id, item_id, &item.owner)?;18541855        // update balance1856        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1857            .checked_sub(1)1858            .ok_or(Error::<T>::NumOverflow)?;1859        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1860        <NftItemList<T>>::remove(collection_id, item_id);18611862        Ok(())1863    }18641865    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1866        ensure!(1867            <FungibleItemList<T>>::contains_key(collection_id, owner),1868            Error::<T>::TokenNotFound1869        );1870        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1871        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18721873        // update balance1874        let new_balance = <Balance<T>>::get(collection_id, owner)1875            .checked_sub(value)1876            .ok_or(Error::<T>::NumOverflow)?;1877        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18781879        if balance.value - value > 0 {1880            balance.value -= value;1881            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1882        }1883        else {1884            <FungibleItemList<T>>::remove(collection_id, owner);1885        }18861887        Ok(())1888    }18891890    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1891        ensure!(1892            <Collection<T>>::contains_key(collection_id),1893            Error::<T>::CollectionNotFound1894        );1895        Ok(())1896    }18971898    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1899        Self::collection_exists(collection_id)?;19001901        let target_collection = <Collection<T>>::get(collection_id);1902        ensure!(1903            subject == target_collection.owner,1904            Error::<T>::NoPermission1905        );19061907        Ok(())1908    }19091910    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1911        let target_collection = <Collection<T>>::get(collection_id);1912        let mut result: bool = subject == target_collection.owner;1913        let exists = <AdminList<T>>::contains_key(collection_id);19141915        if !result & exists {1916            if <AdminList<T>>::get(collection_id).contains(&subject) {1917                result = true1918            }1919        }19201921        result1922    }19231924    fn check_owner_or_admin_permissions(1925        collection_id: CollectionId,1926        subject: T::AccountId,1927    ) -> DispatchResult {1928        Self::collection_exists(collection_id)?;1929        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());19301931        ensure!(1932            result,1933            Error::<T>::NoPermission1934        );1935        Ok(())1936    }19371938    fn owned_amount(1939        subject: T::AccountId,1940        collection_id: CollectionId,1941        item_id: TokenId,1942    ) -> Option<u128> {1943        let target_collection = <Collection<T>>::get(collection_id);19441945        match target_collection.mode {1946            CollectionMode::NFT => {1947                if <NftItemList<T>>::get(collection_id, item_id).owner == subject {1948                    return Some(1)1949                }1950                None1951            },1952            CollectionMode::Fungible(_) => {1953                if <FungibleItemList<T>>::contains_key(collection_id, &subject) {1954                    return Some(<FungibleItemList<T>>::get(collection_id, &subject)1955                        .value);1956                }1957                None1958            },1959            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)1960                .owner1961                .iter()1962                .find(|i| i.owner == subject)1963                .map(|i| i.fraction),1964            CollectionMode::Invalid => None,1965        }1966    }19671968    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1969        let target_collection = <Collection<T>>::get(collection_id);19701971        match target_collection.mode {1972            CollectionMode::NFT => {1973                <NftItemList<T>>::get(collection_id, item_id).owner == subject1974            }1975            CollectionMode::Fungible(_) => {1976                <FungibleItemList<T>>::contains_key(collection_id, &subject)1977            }1978            CollectionMode::ReFungible => {1979                <ReFungibleItemList<T>>::get(collection_id, item_id)1980                    .owner1981                    .iter()1982                    .any(|i| i.owner == subject)1983            }1984            CollectionMode::Invalid => false,1985        }1986    }19871988    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1989        let mes = Error::<T>::AddresNotInWhiteList;1990        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);19911992        Ok(())1993    }19941995    /// Check if token exists. In case of Fungible, check if there is an entry for 1996    /// the owner in fungible balances double map1997    fn token_exists(1998        collection_id: CollectionId,1999        item_id: TokenId,2000        owner: &T::AccountId2001    ) -> DispatchResult {2002        let target_collection = <Collection<T>>::get(collection_id);2003        let exists = match target_collection.mode2004        {2005            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2006            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),2007            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2008            _ => false2009        };20102011        ensure!(exists == true, Error::<T>::TokenNotFound);2012        Ok(())2013    }20142015    fn transfer_fungible(2016        collection_id: CollectionId,2017        value: u128,2018        owner: &T::AccountId,2019        recipient: &T::AccountId,2020    ) -> DispatchResult {2021        Self::token_exists(collection_id, 0, owner)?;20222023        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);2024        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);20252026        // Send balance to recipient (updates balanceOf of recipient)2027        Self::add_fungible_item(collection_id, recipient, value)?;20282029        // update balanceOf of sender2030        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);20312032        // Reduce or remove sender2033        if balance.value == value {2034            <FungibleItemList<T>>::remove(collection_id, owner);2035        }2036        else {2037            balance.value -= value;2038            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);2039        }20402041        Ok(())2042    }20432044    fn transfer_refungible(2045        collection_id: CollectionId,2046        item_id: TokenId,2047        value: u128,2048        owner: T::AccountId,2049        new_owner: T::AccountId,2050    ) -> DispatchResult {2051        Self::token_exists(collection_id, item_id, &owner)?;20522053        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);2054        let item = full_item2055            .owner2056            .iter()2057            .filter(|i| i.owner == owner)2058            .next()2059            .ok_or(Error::<T>::NumOverflow)?;2060        let amount = item.fraction;20612062        ensure!(amount >= value, Error::<T>::TokenValueTooLow);20632064        // update balance2065        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2066            .checked_sub(value)2067            .ok_or(Error::<T>::NumOverflow)?;2068        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20692070        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2071            .checked_add(value)2072            .ok_or(Error::<T>::NumOverflow)?;2073        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20742075        let old_owner = item.owner.clone();2076        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20772078        // transfer2079        if amount == value && !new_owner_has_account {2080            // change owner2081            // new owner do not have account2082            let mut new_full_item = full_item.clone();2083            new_full_item2084                .owner2085                .iter_mut()2086                .find(|i| i.owner == owner)2087                .unwrap()2088                .owner = new_owner.clone();2089            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20902091            // update index collection2092            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2093        } else {2094            let mut new_full_item = full_item.clone();2095            new_full_item2096                .owner2097                .iter_mut()2098                .find(|i| i.owner == owner)2099                .unwrap()2100                .fraction -= value;21012102            // separate amount2103            if new_owner_has_account {2104                // new owner has account2105                new_full_item2106                    .owner2107                    .iter_mut()2108                    .find(|i| i.owner == new_owner)2109                    .unwrap()2110                    .fraction += value;2111            } else {2112                // new owner do not have account2113                new_full_item.owner.push(Ownership {2114                    owner: new_owner.clone(),2115                    fraction: value,2116                });2117                Self::add_token_index(collection_id, item_id, &new_owner)?;2118            }21192120            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2121        }21222123        Ok(())2124    }21252126    fn transfer_nft(2127        collection_id: CollectionId,2128        item_id: TokenId,2129        sender: T::AccountId,2130        new_owner: T::AccountId,2131    ) -> DispatchResult {2132        Self::token_exists(collection_id, item_id, &sender)?;21332134        let mut item = <NftItemList<T>>::get(collection_id, item_id);21352136        ensure!(2137            sender == item.owner,2138            Error::<T>::MustBeTokenOwner2139        );21402141        // update balance2142        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2143            .checked_sub(1)2144            .ok_or(Error::<T>::NumOverflow)?;2145        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);21462147        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2148            .checked_add(1)2149            .ok_or(Error::<T>::NumOverflow)?;2150        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);21512152        // change owner2153        let old_owner = item.owner.clone();2154        item.owner = new_owner.clone();2155        <NftItemList<T>>::insert(collection_id, item_id, item);21562157        // update index collection2158        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21592160        Ok(())2161    }2162    2163    fn set_re_fungible_variable_data(2164        collection_id: CollectionId,2165        item_id: TokenId,2166        data: Vec<u8>2167    ) -> DispatchResult {2168        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);21692170        item.variable_data = data;21712172        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21732174        Ok(())2175    }21762177    fn set_nft_variable_data(2178        collection_id: CollectionId,2179        item_id: TokenId,2180        data: Vec<u8>2181    ) -> DispatchResult {2182        let mut item = <NftItemList<T>>::get(collection_id, item_id);2183        2184        item.variable_data = data;21852186        <NftItemList<T>>::insert(collection_id, item_id, item);2187        2188        Ok(())2189    }21902191    fn init_collection(item: &CollectionType<T::AccountId>) {2192        // check params2193        assert!(2194            item.decimal_points <= MAX_DECIMAL_POINTS,2195            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2196        );2197        assert!(2198            item.name.len() <= 64,2199            "Collection name can not be longer than 63 char"2200        );2201        assert!(2202            item.name.len() <= 256,2203            "Collection description can not be longer than 255 char"2204        );2205        assert!(2206            item.token_prefix.len() <= 16,2207            "Token prefix can not be longer than 15 char"2208        );22092210        // Generate next collection ID2211        let next_id = CreatedCollectionCount::get()2212            .checked_add(1)2213            .unwrap();22142215        CreatedCollectionCount::put(next_id);2216    }22172218    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2219        let current_index = <ItemListIndex>::get(collection_id)2220            .checked_add(1)2221            .unwrap();22222223        let item_owner = item.owner.clone();2224        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22252226        <ItemListIndex>::insert(collection_id, current_index);22272228        // Update balance2229        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2230            .checked_add(1)2231            .unwrap();2232        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2233    }22342235    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2236        let current_index = <ItemListIndex>::get(collection_id)2237            .checked_add(1)2238            .unwrap();22392240        Self::add_token_index(collection_id, current_index, owner).unwrap();22412242        <ItemListIndex>::insert(collection_id, current_index);22432244        // Update balance2245        let new_balance = <Balance<T>>::get(collection_id, owner)2246            .checked_add(item.value)2247            .unwrap();2248        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2249    }22502251    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2252        let current_index = <ItemListIndex>::get(collection_id)2253            .checked_add(1)2254            .unwrap();22552256        let value = item.owner.first().unwrap().fraction;2257        let owner = item.owner.first().unwrap().owner.clone();22582259        Self::add_token_index(collection_id, current_index, &owner).unwrap();22602261        <ItemListIndex>::insert(collection_id, current_index);22622263        // Update balance2264        let new_balance = <Balance<T>>::get(collection_id, &owner)2265            .checked_add(value)2266            .unwrap();2267        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2268    }22692270    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {22712272        // add to account limit2273        if <AccountItemCount<T>>::contains_key(owner) {22742275            // bound Owned tokens by a single address2276            let count = <AccountItemCount<T>>::get(owner);2277            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);22782279            <AccountItemCount<T>>::insert(owner.clone(), count2280                .checked_add(1)2281                .ok_or(Error::<T>::NumOverflow)?);2282        }2283        else {2284            <AccountItemCount<T>>::insert(owner.clone(), 1);2285        }22862287        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2288        if list_exists {2289            let mut list = <AddressTokens<T>>::get(collection_id, owner);2290            let item_contains = list.contains(&item_index.clone());22912292            if !item_contains {2293                list.push(item_index.clone());2294            }22952296            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2297        } else {2298            let mut itm = Vec::new();2299            itm.push(item_index.clone());2300            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2301        }23022303        Ok(())2304    }23052306    fn remove_token_index(2307        collection_id: CollectionId,2308        item_index: TokenId,2309        owner: &T::AccountId,2310    ) -> DispatchResult {23112312        // update counter2313        <AccountItemCount<T>>::insert(owner.clone(), 2314            <AccountItemCount<T>>::get(owner)2315            .checked_sub(1)2316            .ok_or(Error::<T>::NumOverflow)?);231723182319        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2320        if list_exists {2321            let mut list = <AddressTokens<T>>::get(collection_id, owner);2322            let item_contains = list.contains(&item_index.clone());23232324            if item_contains {2325                list.retain(|&item| item != item_index);2326                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2327            }2328        }23292330        Ok(())2331    }23322333    fn move_token_index(2334        collection_id: CollectionId,2335        item_index: TokenId,2336        old_owner: &T::AccountId,2337        new_owner: &T::AccountId,2338    ) -> DispatchResult {2339        Self::remove_token_index(collection_id, item_index, old_owner)?;2340        Self::add_token_index(collection_id, item_index, new_owner)?;23412342        Ok(())2343    }2344    2345    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2346        if <ContractOwner<T>>::contains_key(contract.clone()) {2347            let owner = <ContractOwner<T>>::get(contract);2348            ensure!(account == owner, Error::<T>::NoPermission);2349        } else {2350            fail!(Error::<T>::NoPermission);2351        }23522353        Ok(())2354    }2355}23562357////////////////////////////////////////////////////////////////////////////////////////////////////2358// Economic models2359// #region23602361/// Fee multiplier.2362pub type Multiplier = FixedU128;23632364type BalanceOf<T> = <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::Balance;23652366/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2367/// in the queue.2368#[derive(Encode, Decode, Clone, Eq, PartialEq)]2369pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);23702371impl<T: Config + Send + Sync> sp_std::fmt::Debug 2372    for ChargeTransactionPayment<T>2373{2374	#[cfg(feature = "std")]2375	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2376		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2377	}2378	#[cfg(not(feature = "std"))]2379	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2380		Ok(())2381	}2382}23832384impl<T: Config> ChargeTransactionPayment<T>2385where2386    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2387    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2388    T::AccountId: AsRef<[u8]>,2389    T::AccountId: UncheckedFrom<T::Hash>,2390{2391    fn traditional_fee(2392        len: usize,2393        info: &DispatchInfoOf<T::Call>,2394        tip: BalanceOf<T>,2395    ) -> BalanceOf<T>2396    where2397        T::Call: Dispatchable<Info = DispatchInfo>,2398    {2399        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2400    }24012402	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2403        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2404        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2405        let len_saturation = max_block_length as u64 / (len as u64).max(1);2406        let coefficient: BalanceOf<T> = weight_saturation2407            .min(len_saturation)2408            .saturated_into::<BalanceOf<T>>();2409        final_fee2410            .saturating_mul(coefficient)2411            .saturated_into::<TransactionPriority>()2412    }24132414    fn withdraw_fee(2415        &self,2416        who: &T::AccountId,2417        call: &T::Call,2418        info: &DispatchInfoOf<T::Call>,2419        len: usize,2420	) -> Result<2421		(2422			BalanceOf<T>,2423			<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2424		),2425		TransactionValidityError,2426	> {2427        let tip = self.0;24282429        // Set fee based on call type. Creating collection costs 1 Unique.2430        // All other transactions have traditional fees so far2431        // let fee = match call.is_sub_type() {2432        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2433        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2434        //                                                 // _ => <BalanceOf<T>>::from(100)2435        // };2436        let fee = Self::traditional_fee(len, info, tip);24372438        // Only mess with balances if fee is not zero.2439        if fee.is_zero() {2440            return <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2441			.map(|i| (fee, i));2442        }24432444        // Determine who is paying transaction fee based on ecnomic model2445        // Parse call to extract collection ID and access collection sponsor2446        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2447            Some(Call::create_item(collection_id, _owner, _properties)) => {24482449                // sponsor timeout2450                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;24512452                let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;2453                let mut sponsored = true;2454                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2455                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2456                    let limit_time = last_tx_block + limit.into();2457                    if block_number <= limit_time {2458                        sponsored = false;2459                    }2460                }2461                if sponsored {2462                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2463                }24642465                // check free create limit2466                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2467                   (<Collection<T>>::get(collection_id).sponsor_confirmed) &&2468                   (sponsored)2469                {2470                    <Collection<T>>::get(collection_id).sponsor2471                } else {2472                    T::AccountId::default()2473                }2474            }2475            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2476                2477                let mut sponsor_transfer = false;2478                if <Collection<T>>::get(collection_id).sponsor_confirmed {24792480                    let collection_limits = <Collection<T>>::get(collection_id).limits;2481                    let collection_mode = <Collection<T>>::get(collection_id).mode;2482    2483                    // sponsor timeout2484                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2485                    sponsor_transfer = match collection_mode {2486                        CollectionMode::NFT => {2487    2488                            // get correct limit2489                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2490                                collection_limits.sponsor_transfer_timeout2491                            } else {2492                                ChainLimit::get().nft_sponsor_transfer_timeout2493                            };2494    2495                            let mut sponsored = true;2496                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2497                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2498                                let limit_time = last_tx_block + limit.into();2499                                if block_number <= limit_time {2500                                    sponsored = false;2501                                }2502                            }2503                            if sponsored {2504                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2505                            }25062507                            sponsored2508                        }2509                        CollectionMode::Fungible(_) => {2510    2511                            // get correct limit2512                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2513                                collection_limits.sponsor_transfer_timeout2514                            } else {2515                                ChainLimit::get().fungible_sponsor_transfer_timeout2516                            };2517    2518                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2519                            let mut sponsored = true;2520                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2521                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2522                                let limit_time = last_tx_block + limit.into();2523                                if block_number <= limit_time {2524                                    sponsored = false;2525                                }2526                            }2527                            if sponsored {2528                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2529                            }25302531                            sponsored2532                        }2533                        CollectionMode::ReFungible => {2534    2535                            // get correct limit2536                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2537                                collection_limits.sponsor_transfer_timeout2538                            } else {2539                                ChainLimit::get().refungible_sponsor_transfer_timeout2540                            };2541    2542                            let mut sponsored = true;2543                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2544                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2545                                let limit_time = last_tx_block + limit.into();2546                                if block_number <= limit_time {2547                                    sponsored = false;2548                                }2549                            }2550                            if sponsored {2551                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2552                            }25532554                            sponsored2555                        }2556                        _ => {2557                            false2558                        },2559                    };2560                }25612562                if !sponsor_transfer {2563                    T::AccountId::default()2564                } else {2565                    <Collection<T>>::get(collection_id).sponsor2566                }2567            }25682569            _ => T::AccountId::default(),2570        };25712572        // Sponsor smart contracts2573        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {25742575            // On instantiation: set the contract owner2576            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {25772578                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2579                    &who,2580                    code_hash,2581                    salt,2582                );2583                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());25842585                T::AccountId::default()2586            },25872588            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2589            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {25902591                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());25922593                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2594                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2595                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2596                  2597                if !owned_contract && white_list_enabled {2598                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2599                        return Err(InvalidTransaction::Call.into());2600                    }2601                }26022603                let mut sponsor_transfer = false;2604                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2605                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2606                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2607                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2608                    let limit_time = last_tx_block + rate_limit;26092610                    if block_number >= limit_time {2611                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2612                        sponsor_transfer = true;2613                    }2614                } else {2615                    sponsor_transfer = false;2616                }2617               2618                2619                let mut sp = T::AccountId::default();2620                if sponsor_transfer {2621                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2622                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2623                            sp = called_contract;2624                        }2625                    }2626                }26272628                sp2629            },26302631            _ => sponsor,2632        };26332634        let mut who_pays_fee: T::AccountId = sponsor.clone();2635        if sponsor == T::AccountId::default() {2636            who_pays_fee = who.clone();2637        }26382639		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2640			.map(|i| (fee, i))2641    }2642}264326442645impl<T: Config + Send + Sync> SignedExtension2646    for ChargeTransactionPayment<T>2647where2648    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2649    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2650    T::AccountId: AsRef<[u8]>,2651    T::AccountId: UncheckedFrom<T::Hash>,2652{2653    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2654    type AccountId = T::AccountId;2655    type Call = T::Call;2656    type AdditionalSigned = ();2657    type Pre = (2658        // tip2659        BalanceOf<T>,2660        // who pays fee2661        Self::AccountId,2662		// imbalance resulting from withdrawing the fee2663		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2664    );2665    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2666        Ok(())2667    }26682669    fn validate(2670        &self,2671        who: &Self::AccountId,2672        call: &Self::Call,2673        info: &DispatchInfoOf<Self::Call>,2674        len: usize,2675    ) -> TransactionValidity {2676		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2677		Ok(ValidTransaction {2678			priority: Self::get_priority(len, info, fee),2679			..Default::default()2680		})2681    }26822683    fn pre_dispatch(2684        self,2685        who: &Self::AccountId,2686        call: &Self::Call,2687        info: &DispatchInfoOf<Self::Call>,2688        len: usize,2689    ) -> Result<Self::Pre, TransactionValidityError> {2690        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2691        Ok((self.0, who.clone(), imbalance))2692    }26932694    fn post_dispatch(2695        pre: Self::Pre,2696        info: &DispatchInfoOf<Self::Call>,2697        post_info: &PostDispatchInfoOf<Self::Call>,2698        len: usize,2699        _result: &DispatchResult,2700    ) -> Result<(), TransactionValidityError> {2701		let (tip, who, imbalance) = pre;2702		let actual_fee = transaction_payment::Module::<T>::compute_actual_fee(2703			len as u32,2704			info,2705			post_info,2706			tip,2707		);2708		<T as transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2709		Ok(())2710    }2711}27122713// #endregion