git.delta.rocks / unique-network / refs/commits / 5795d313a129

difftreelog

source

pallets/nft/src/lib.rs99.2 KiBsourcehistory
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, IsSubType,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial, DispatchClass,29    },30    StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use sp_runtime::traits::StaticLookup;45use pallet_contracts::chain_extension::UncheckedFrom;46use transaction_payment::OnChargeTransaction;4748#[cfg(test)]49mod mock;5051#[cfg(test)]52mod tests;5354mod default_weights;5556pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;57pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;58pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;59pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6061// Structs62// #region6364pub type CollectionId = u32;65pub type TokenId = u32;66pub type DecimalPoints = u8;6768#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]69#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]70pub enum CollectionMode {71    Invalid,72    NFT,73    // decimal points74    Fungible(DecimalPoints),75    ReFungible,76}7778impl Default for CollectionMode {79    fn default() -> Self {80        Self::Invalid81    }82}8384impl Into<u8> for CollectionMode {85    fn into(self) -> u8 {86        match self {87            CollectionMode::Invalid => 0,88            CollectionMode::NFT => 1,89            CollectionMode::Fungible(_) => 2,90            CollectionMode::ReFungible => 3,91        }92    }93}9495#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]96#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]97pub enum AccessMode {98    Normal,99    WhiteList,100}101impl Default for AccessMode {102    fn default() -> Self {103        Self::Normal104    }105}106107#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]108#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]109pub enum SchemaVersion {110    ImageURL,111    Unique,112}113impl Default for SchemaVersion {114    fn default() -> Self {115        Self::ImageURL116    }117}118119#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]120#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]121pub struct Ownership<AccountId> {122    pub owner: AccountId,123    pub fraction: u128,124}125126#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub struct CollectionType<AccountId> {129    pub owner: AccountId,130    pub mode: CollectionMode,131    pub access: AccessMode,132    pub decimal_points: DecimalPoints,133    pub name: Vec<u16>,        // 64 include null escape char134    pub description: Vec<u16>, // 256 include null escape char135    pub token_prefix: Vec<u8>, // 16 include null escape char136    pub mint_mode: bool,137    pub offchain_schema: Vec<u8>,138    pub schema_version: SchemaVersion,139    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender140    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.141    pub limits: CollectionLimits, // Collection private restrictions 142    pub variable_on_chain_schema: Vec<u8>, //143    pub const_on_chain_schema: Vec<u8>, //144}145146#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]147#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]148pub struct NftItemType<AccountId> {149    pub owner: AccountId,150    pub const_data: Vec<u8>,151    pub variable_data: Vec<u8>,152}153154#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]155#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]156pub struct FungibleItemType {157    pub value: u128,158}159160#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]161#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]162pub struct ReFungibleItemType<AccountId> {163    pub owner: Vec<Ownership<AccountId>>,164    pub const_data: Vec<u8>,165    pub variable_data: Vec<u8>,166}167168// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]169// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170// pub struct VestingItem<AccountId, Moment> {171//     pub sender: AccountId,172//     pub recipient: AccountId,173//     pub collection_id: CollectionId,174//     pub item_id: TokenId,175//     pub amount: u64,176//     pub vesting_date: Moment,177// }178179#[derive(Encode, Decode, Debug, Clone, PartialEq)]180#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]181pub struct CollectionLimits {182    pub account_token_ownership_limit: u32,183    pub sponsored_data_size: u32,184    pub token_limit: u32,185186    // Timeouts for item types in passed blocks187    pub sponsor_transfer_timeout: u32,188    pub owner_can_transfer: bool,189    pub owner_can_destroy: bool,190}191192impl Default for CollectionLimits {193    fn default() -> CollectionLimits {194        CollectionLimits { 195            account_token_ownership_limit: 10_000_000, 196            token_limit: u32::max_value(),197            sponsored_data_size: u32::max_value(), 198            sponsor_transfer_timeout: 14400,199            owner_can_transfer: true,200            owner_can_destroy: true201        }202    }203}204205#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]206#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]207pub struct ChainLimits {208    pub collection_numbers_limit: u32,209    pub account_token_ownership_limit: u32,210    pub collections_admins_limit: u64,211    pub custom_data_limit: u32,212213    // Timeouts for item types in passed blocks214    pub nft_sponsor_transfer_timeout: u32,215    pub fungible_sponsor_transfer_timeout: u32,216    pub refungible_sponsor_transfer_timeout: u32,217218    // Schema limits219    pub offchain_schema_limit: u32,220    pub variable_on_chain_schema_limit: u32,221    pub const_on_chain_schema_limit: u32,222}223224pub trait WeightInfo {225	fn create_collection() -> Weight;226	fn destroy_collection() -> Weight;227	fn add_to_white_list() -> Weight;228	fn remove_from_white_list() -> Weight;229    fn set_public_access_mode() -> Weight;230    fn set_mint_permission() -> Weight;231    fn change_collection_owner() -> Weight;232    fn add_collection_admin() -> Weight;233    fn remove_collection_admin() -> Weight;234    fn set_collection_sponsor() -> Weight;235    fn confirm_sponsorship() -> Weight;236    fn remove_collection_sponsor() -> Weight;237    fn create_item(s: usize) -> Weight;238    fn burn_item() -> Weight;239    fn transfer() -> Weight;240    fn approve() -> Weight;241    fn transfer_from() -> Weight;242    fn set_offchain_schema() -> Weight;243    fn set_const_on_chain_schema() -> Weight;244    fn set_variable_on_chain_schema() -> Weight;245    fn set_variable_meta_data() -> Weight;246    fn enable_contract_sponsoring() -> Weight;247    fn set_schema_version() -> Weight;248    fn set_chain_limits() -> Weight;249    fn set_contract_sponsoring_rate_limit() -> Weight;250    fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;251    fn toggle_contract_white_list() -> Weight;252    fn add_to_contract_white_list() -> Weight;253    fn remove_from_contract_white_list() -> Weight;254    fn set_collection_limits() -> Weight;255}256257#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]258#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]259pub struct CreateNftData {260    pub const_data: Vec<u8>,261    pub variable_data: Vec<u8>,262}263264#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]265#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]266pub struct CreateFungibleData {267    pub value: u128,268}269270#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]271#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]272pub struct CreateReFungibleData {273    pub const_data: Vec<u8>,274    pub variable_data: Vec<u8>,275    pub pieces: u128,276}277278#[derive(Encode, Decode, Debug, Clone, PartialEq)]279#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]280pub enum CreateItemData {281    NFT(CreateNftData),282    Fungible(CreateFungibleData),283    ReFungible(CreateReFungibleData),284}285286impl CreateItemData {287    pub fn len(&self) -> usize {288        let len = match self {289            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),290            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),291            _ => 0292        };293        294        return len;295    }296}297298impl From<CreateNftData> for CreateItemData {299    fn from(item: CreateNftData) -> Self {300        CreateItemData::NFT(item)301    }302}303304impl From<CreateReFungibleData> for CreateItemData {305    fn from(item: CreateReFungibleData) -> Self {306        CreateItemData::ReFungible(item)307    }308}309310impl From<CreateFungibleData> for CreateItemData {311    fn from(item: CreateFungibleData) -> Self {312        CreateItemData::Fungible(item)313    }314}315316317decl_error! {318	/// Error for non-fungible-token module.319	pub enum Error for Module<T: Config> {320        /// Total collections bound exceeded.321        TotalCollectionsLimitExceeded,322		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.323        CollectionDecimalPointLimitExceeded, 324        /// Collection name can not be longer than 63 char.325        CollectionNameLimitExceeded, 326        /// Collection description can not be longer than 255 char.327        CollectionDescriptionLimitExceeded, 328        /// Token prefix can not be longer than 15 char.329        CollectionTokenPrefixLimitExceeded,330        /// This collection does not exist.331        CollectionNotFound,332        /// Item not exists.333        TokenNotFound,334        /// Admin not found335        AdminNotFound,336        /// Arithmetic calculation overflow.337        NumOverflow,       338        /// Account already has admin role.339        AlreadyAdmin,  340        /// You do not own this collection.341        NoPermission,342        /// This address is not set as sponsor, use setCollectionSponsor first.343        ConfirmUnsetSponsorFail,344        /// Collection is not in mint mode.345        PublicMintingNotAllowed,346        /// Sender parameter and item owner must be equal.347        MustBeTokenOwner,348        /// Item balance not enough.349        TokenValueTooLow,350        /// Size of item is too large.351        NftSizeLimitExceeded,352        /// No approve found353        ApproveNotFound,354        /// Requested value more than approved.355        TokenValueNotEnough,356        /// Only approved addresses can call this method.357        ApproveRequired,358        /// Address is not in white list.359        AddresNotInWhiteList,360        /// Number of collection admins bound exceeded.361        CollectionAdminsLimitExceeded,362        /// Owned tokens by a single address bound exceeded.363        AddressOwnershipLimitExceeded,364        /// Length of items properties must be greater than 0.365        EmptyArgument,366        /// const_data exceeded data limit.367        TokenConstDataLimitExceeded,368        /// variable_data exceeded data limit.369        TokenVariableDataLimitExceeded,370        /// Not NFT item data used to mint in NFT collection.371        NotNftDataUsedToMintNftCollectionToken,372        /// Not Fungible item data used to mint in Fungible collection.373        NotFungibleDataUsedToMintFungibleCollectionToken,374        /// Not Re Fungible item data used to mint in Re Fungible collection.375        NotReFungibleDataUsedToMintReFungibleCollectionToken,376        /// Unexpected collection type.377        UnexpectedCollectionType,378        /// Can't store metadata in fungible tokens.379        CantStoreMetadataInFungibleTokens,380        /// Collection token limit exceeded381        CollectionTokenLimitExceeded,382        /// Account token limit exceeded per collection383        AccountTokenLimitExceeded,384        /// Collection limit bounds per collection exceeded385        CollectionLimitBoundsExceeded,386        /// Schema data size limit bound exceeded387        SchemaDataLimitExceeded,388        /// Maximum refungibility exceeded389        WrongRefungiblePieces390	}391}392393pub trait Config: system::Config + Sized + transaction_payment::Config + pallet_contracts::Config {394    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;395396    /// Weight information for extrinsics in this pallet.397	type WeightInfo: WeightInfo;398}399400#[cfg(feature = "runtime-benchmarks")]401mod benchmarking;402403// #endregion404405decl_storage! {406    trait Store for Module<T: Config> as Nft {407408        // Private members409        NextCollectionID: CollectionId;410        CreatedCollectionCount: u32;411        ChainVersion: u64;412        ItemListIndex: map hasher(identity) CollectionId => TokenId;413414        // Chain limits struct415        pub ChainLimit get(fn chain_limit) config(): ChainLimits;416417        // Bound counters418        CollectionCount: u32;419        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;420421        // Basic collections422        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;423        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;424        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;425426        /// Balance owner per collection map427        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;428429        /// second parameter: item id + owner account id + spender account id430        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;431432        /// Item collections433        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;434        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;435        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;436437        /// Index list438        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;439440        /// Tokens transfer baskets441        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;442        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;443        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;444        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;445446        /// Variable metadata sponsoring447        pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Option<T::BlockNumber> = None;448        pub VariableMetaDataSponsoringRateLimit get(fn variable_meta_data_sponsoring_rate_limit): map hasher(identity) CollectionId => T::BlockNumber = 0.into();449450        // Contract Sponsorship and Ownership451        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;452        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;453        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;454        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;455        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 456        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 457    }458    add_extra_genesis {459        build(|config: &GenesisConfig<T>| {460            // Modification of storage461            for (_num, _c) in &config.collection {462                <Module<T>>::init_collection(_c);463            }464465            for (_num, _c, _i) in &config.nft_item_id {466                <Module<T>>::init_nft_token(*_c, _i);467            }468469            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {470                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);471            }472473            for (_num, _c, _i) in &config.refungible_item_id {474                <Module<T>>::init_refungible_token(*_c, _i);475            }476        })477    }478}479480decl_event!(481    pub enum Event<T>482    where483        AccountId = <T as system::Config>::AccountId,484    {485        /// New collection was created486        /// 487        /// # Arguments488        /// 489        /// * collection_id: Globally unique identifier of newly created collection.490        /// 491        /// * mode: [CollectionMode] converted into u8.492        /// 493        /// * account_id: Collection owner.494        Created(CollectionId, u8, AccountId),495496        /// New item was created.497        /// 498        /// # Arguments499        /// 500        /// * collection_id: Id of the collection where item was created.501        /// 502        /// * item_id: Id of an item. Unique within the collection.503        ItemCreated(CollectionId, TokenId),504505        /// Collection item was burned.506        /// 507        /// # Arguments508        /// 509        /// collection_id.510        /// 511        /// item_id: Identifier of burned NFT.512        ItemDestroyed(CollectionId, TokenId),513    }514);515516decl_module! {517    pub struct Module<T: Config> for enum Call 518    where 519        origin: T::Origin520    {521        fn deposit_event() = default;522        type Error = Error<T>;523524        fn on_initialize(now: T::BlockNumber) -> Weight {525526            if ChainVersion::get() < 2527            {528                let value = NextCollectionID::get();529                CreatedCollectionCount::put(value);530                ChainVersion::put(2);531            }532533            0534        }535536        /// 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.537        /// 538        /// # Permissions539        /// 540        /// * Anyone.541        /// 542        /// # Arguments543        /// 544        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.545        /// 546        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.547        /// 548        /// * token_prefix: UTF-8 string with token prefix.549        /// 550        /// * mode: [CollectionMode] collection type and type dependent data.551        // returns collection ID552        #[weight = <T as Config>::WeightInfo::create_collection()]553        pub fn create_collection(origin,554                                 collection_name: Vec<u16>,555                                 collection_description: Vec<u16>,556                                 token_prefix: Vec<u8>,557                                 mode: CollectionMode) -> DispatchResult {558559            // Anyone can create a collection560            let who = ensure_signed(origin)?;561562            let decimal_points = match mode {563                CollectionMode::Fungible(points) => points,564                _ => 0565            };566567            // bound Total number of collections568            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);569570            // check params571            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);572            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);573            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);574            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);575576            // Generate next collection ID577            let next_id = CreatedCollectionCount::get()578                .checked_add(1)579                .ok_or(Error::<T>::NumOverflow)?;580581            // bound counter582            let total = CollectionCount::get()583                .checked_add(1)584                .ok_or(Error::<T>::NumOverflow)?;585586            CreatedCollectionCount::put(next_id);587            CollectionCount::put(total);588589            // Create new collection590            let new_collection = CollectionType {591                owner: who.clone(),592                name: collection_name,593                mode: mode.clone(),594                mint_mode: false,595                access: AccessMode::Normal,596                description: collection_description,597                decimal_points: decimal_points,598                token_prefix: token_prefix,599                offchain_schema: Vec::new(),600                schema_version: SchemaVersion::ImageURL,601                sponsor: T::AccountId::default(),602                sponsor_confirmed: false,603                variable_on_chain_schema: Vec::new(),604                const_on_chain_schema: Vec::new(),605                limits: CollectionLimits::default()606            };607608            // Add new collection to map609            <Collection<T>>::insert(next_id, new_collection);610611            // call event612            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));613614            Ok(())615        }616617        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.618        /// 619        /// # Permissions620        /// 621        /// * Collection Owner.622        /// 623        /// # Arguments624        /// 625        /// * collection_id: collection to destroy.626        #[weight = <T as Config>::WeightInfo::destroy_collection()]627        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {628629            let sender = ensure_signed(origin)?;630            Self::check_owner_permissions(collection_id, sender)?;631632            <AddressTokens<T>>::remove_prefix(collection_id);633            <Allowances<T>>::remove_prefix(collection_id);634            <Balance<T>>::remove_prefix(collection_id);635            <ItemListIndex>::remove(collection_id);636            <AdminList<T>>::remove(collection_id);637            <Collection<T>>::remove(collection_id);638            <WhiteList<T>>::remove_prefix(collection_id);639640            <NftItemList<T>>::remove_prefix(collection_id);641            <FungibleItemList<T>>::remove_prefix(collection_id);642            <ReFungibleItemList<T>>::remove_prefix(collection_id);643644            <NftTransferBasket<T>>::remove_prefix(collection_id);645            <FungibleTransferBasket<T>>::remove_prefix(collection_id);646            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);647648            <VariableMetaDataBasket<T>>::remove_prefix(collection_id);649            <VariableMetaDataSponsoringRateLimit<T>>::remove(collection_id);650651            if CollectionCount::get() > 0652            {653                // bound couter654                let total = CollectionCount::get()655                    .checked_sub(1)656                    .ok_or(Error::<T>::NumOverflow)?;657658                CollectionCount::put(total);659            }660661            Ok(())662        }663664        /// Add an address to white list.665        /// 666        /// # Permissions667        /// 668        /// * Collection Owner669        /// * Collection Admin670        /// 671        /// # Arguments672        /// 673        /// * collection_id.674        /// 675        /// * address.676        #[weight = <T as Config>::WeightInfo::add_to_white_list()]677        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{678679            let sender = ensure_signed(origin)?;680            Self::check_owner_or_admin_permissions(collection_id, sender)?;681682            <WhiteList<T>>::insert(collection_id, address, true);683            684            Ok(())685        }686687        /// Remove an address from white list.688        /// 689        /// # Permissions690        /// 691        /// * Collection Owner692        /// * Collection Admin693        /// 694        /// # Arguments695        /// 696        /// * collection_id.697        /// 698        /// * address.699        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]700        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{701702            let sender = ensure_signed(origin)?;703            Self::check_owner_or_admin_permissions(collection_id, sender)?;704705            <WhiteList<T>>::remove(collection_id, address);706707            Ok(())708        }709710        /// Toggle between normal and white list access for the methods with access for `Anyone`.711        /// 712        /// # Permissions713        /// 714        /// * Collection Owner.715        /// 716        /// # Arguments717        /// 718        /// * collection_id.719        /// 720        /// * mode: [AccessMode]721        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]722        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult723        {724            let sender = ensure_signed(origin)?;725726            Self::check_owner_permissions(collection_id, sender)?;727            let mut target_collection = <Collection<T>>::get(collection_id);728            target_collection.access = mode;729            <Collection<T>>::insert(collection_id, target_collection);730731            Ok(())732        }733734        /// Allows Anyone to create tokens if:735        /// * White List is enabled, and736        /// * Address is added to white list, and737        /// * This method was called with True parameter738        /// 739        /// # Permissions740        /// * Collection Owner741        ///742        /// # Arguments743        /// 744        /// * collection_id.745        /// 746        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.747        #[weight = <T as Config>::WeightInfo::set_mint_permission()]748        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult749        {750            let sender = ensure_signed(origin)?;751752            Self::check_owner_permissions(collection_id, sender)?;753            let mut target_collection = <Collection<T>>::get(collection_id);754            target_collection.mint_mode = mint_permission;755            <Collection<T>>::insert(collection_id, target_collection);756757            Ok(())758        }759760        /// Change the owner of the collection.761        /// 762        /// # Permissions763        /// 764        /// * Collection Owner.765        /// 766        /// # Arguments767        /// 768        /// * collection_id.769        /// 770        /// * new_owner.771        #[weight = <T as Config>::WeightInfo::change_collection_owner()]772        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {773774            let sender = ensure_signed(origin)?;775            Self::check_owner_permissions(collection_id, sender)?;776            let mut target_collection = <Collection<T>>::get(collection_id);777            target_collection.owner = new_owner;778            <Collection<T>>::insert(collection_id, target_collection);779780            Ok(())781        }782783        /// Adds an admin of the Collection.784        /// 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. 785        /// 786        /// # Permissions787        /// 788        /// * Collection Owner.789        /// * Collection Admin.790        /// 791        /// # Arguments792        /// 793        /// * collection_id: ID of the Collection to add admin for.794        /// 795        /// * new_admin_id: Address of new admin to add.796        #[weight = <T as Config>::WeightInfo::add_collection_admin()]797        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {798799            let sender = ensure_signed(origin)?;800            Self::check_owner_or_admin_permissions(collection_id, sender)?;801            let mut admin_arr: Vec<T::AccountId> = Vec::new();802803            if <AdminList<T>>::contains_key(collection_id)804            {805                admin_arr = <AdminList<T>>::get(collection_id);806                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);807            }808809            // Number of collection admins810            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);811812            admin_arr.push(new_admin_id);813            <AdminList<T>>::insert(collection_id, admin_arr);814815            Ok(())816        }817818        /// 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.819        ///820        /// # Permissions821        /// 822        /// * Collection Owner.823        /// * Collection Admin.824        /// 825        /// # Arguments826        /// 827        /// * collection_id: ID of the Collection to remove admin for.828        /// 829        /// * account_id: Address of admin to remove.830        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]831        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {832833            let sender = ensure_signed(origin)?;834            Self::check_owner_or_admin_permissions(collection_id, sender)?;835            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);836837            let mut admin_arr = <AdminList<T>>::get(collection_id);838            admin_arr.retain(|i| *i != account_id);839            <AdminList<T>>::insert(collection_id, admin_arr);840841            Ok(())842        }843844        /// # Permissions845        /// 846        /// * Collection Owner847        /// 848        /// # Arguments849        /// 850        /// * collection_id.851        /// 852        /// * new_sponsor.853        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]854        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {855856            let sender = ensure_signed(origin)?;857            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);858859            let mut target_collection = <Collection<T>>::get(collection_id);860            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);861862            target_collection.sponsor = new_sponsor;863            target_collection.sponsor_confirmed = false;864            <Collection<T>>::insert(collection_id, target_collection);865866            Ok(())867        }868869        /// # Permissions870        /// 871        /// * Sponsor.872        /// 873        /// # Arguments874        /// 875        /// * collection_id.876        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]877        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {878879            let sender = ensure_signed(origin)?;880            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);881882            let mut target_collection = <Collection<T>>::get(collection_id);883            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);884885            target_collection.sponsor_confirmed = true;886            <Collection<T>>::insert(collection_id, target_collection);887888            Ok(())889        }890891        /// Switch back to pay-per-own-transaction model.892        ///893        /// # Permissions894        ///895        /// * Collection owner.896        /// 897        /// # Arguments898        /// 899        /// * collection_id.900        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]901        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {902903            let sender = ensure_signed(origin)?;904            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);905906            let mut target_collection = <Collection<T>>::get(collection_id);907            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);908909            target_collection.sponsor = T::AccountId::default();910            target_collection.sponsor_confirmed = false;911            <Collection<T>>::insert(collection_id, target_collection);912913            Ok(())914        }915916        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.917        /// 918        /// # Permissions919        /// 920        /// * Collection Owner.921        /// * Collection Admin.922        /// * Anyone if923        ///     * White List is enabled, and924        ///     * Address is added to white list, and925        ///     * MintPermission is enabled (see SetMintPermission method)926        /// 927        /// # Arguments928        /// 929        /// * collection_id: ID of the collection.930        /// 931        /// * owner: Address, initial owner of the NFT.932        ///933        /// * data: Token data to store on chain.934        // #[weight =935        // (130_000_000 as Weight)936        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))937        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))938        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]939940        #[weight = <T as Config>::WeightInfo::create_item(data.len())]941        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {942943            let sender = ensure_signed(origin)?;944945            Self::collection_exists(collection_id)?;946947            let target_collection = <Collection<T>>::get(collection_id);948949            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;950            Self::validate_create_item_args(&target_collection, &data)?;951            Self::create_item_no_validation(collection_id, owner, data)?;952953            Ok(())954        }955956        /// This method creates multiple instances of NFT Collection created with CreateCollection method.957        /// 958        /// # Permissions959        /// 960        /// * Collection Owner.961        /// * Collection Admin.962        /// * Anyone if963        ///     * White List is enabled, and964        ///     * Address is added to white list, and965        ///     * MintPermission is enabled (see SetMintPermission method)966        /// 967        /// # Arguments968        /// 969        /// * collection_id: ID of the collection.970        /// 971        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].972        /// 973        /// * owner: Address, initial owner of the NFT.974        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()975                               .map(|data| { data.len() })976                               .sum())]977        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {978979            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);980            let sender = ensure_signed(origin)?;981982            Self::collection_exists(collection_id)?;983            let target_collection = <Collection<T>>::get(collection_id);984985            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;986987            for data in &items_data {988                Self::validate_create_item_args(&target_collection, data)?;989            }990            for data in &items_data {991                Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?;992            }993994            Ok(())995        }996997        /// Destroys a concrete instance of NFT.998        /// 999        /// # Permissions1000        /// 1001        /// * Collection Owner.1002        /// * Collection Admin.1003        /// * Current NFT Owner.1004        /// 1005        /// # Arguments1006        /// 1007        /// * collection_id: ID of the collection.1008        /// 1009        /// * item_id: ID of NFT to burn.1010        #[weight = <T as Config>::WeightInfo::burn_item()]1011        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10121013            let sender = ensure_signed(origin)?;1014            Self::collection_exists(collection_id)?;10151016            // Transfer permissions check1017            let target_collection = <Collection<T>>::get(collection_id);1018            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1019                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1020                Error::<T>::NoPermission);10211022            if target_collection.access == AccessMode::WhiteList {1023                Self::check_white_list(collection_id, &sender)?;1024            }10251026            match target_collection.mode1027            {1028                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1029                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1030                CollectionMode::ReFungible  => Self::burn_refungible_item(collection_id, item_id, &sender)?,1031                _ => ()1032            };10331034            // call event1035            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10361037            Ok(())1038        }10391040        /// Change ownership of the token.1041        /// 1042        /// # Permissions1043        /// 1044        /// * Collection Owner1045        /// * Collection Admin1046        /// * Current NFT owner1047        ///1048        /// # Arguments1049        /// 1050        /// * recipient: Address of token recipient.1051        /// 1052        /// * collection_id.1053        /// 1054        /// * item_id: ID of the item1055        ///     * Non-Fungible Mode: Required.1056        ///     * Fungible Mode: Ignored.1057        ///     * Re-Fungible Mode: Required.1058        /// 1059        /// * value: Amount to transfer.1060        ///     * Non-Fungible Mode: Ignored1061        ///     * Fungible Mode: Must specify transferred amount1062        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1063        #[weight = <T as Config>::WeightInfo::transfer()]1064        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1065            let sender = ensure_signed(origin)?;1066            Self::transfer_internal(sender, recipient, collection_id, item_id, value)1067        }10681069        /// Set, change, or remove approved address to transfer the ownership of the NFT.1070        /// 1071        /// # Permissions1072        /// 1073        /// * Collection Owner1074        /// * Collection Admin1075        /// * Current NFT owner1076        /// 1077        /// # Arguments1078        /// 1079        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1080        /// 1081        /// * collection_id.1082        /// 1083        /// * item_id: ID of the item.1084        #[weight = <T as Config>::WeightInfo::approve()]1085        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {10861087            let sender = ensure_signed(origin)?;10881089            Self::collection_exists(collection_id)?;1090            Self::token_exists(collection_id, item_id, &sender)?;10911092            // Transfer permissions check1093            let target_collection = <Collection<T>>::get(collection_id);1094            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1095                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1096                Error::<T>::NoPermission);10971098            if target_collection.access == AccessMode::WhiteList {1099                Self::check_white_list(collection_id, &sender)?;1100                Self::check_white_list(collection_id, &spender)?;1101            }11021103            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1104            let mut allowance: u128 = amount;1105            if allowance_exists {1106                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1107            }1108            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11091110            Ok(())1111        }1112        1113        /// 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.1114        /// 1115        /// # Permissions1116        /// * Collection Owner1117        /// * Collection Admin1118        /// * Current NFT owner1119        /// * Address approved by current NFT owner1120        /// 1121        /// # Arguments1122        /// 1123        /// * from: Address that owns token.1124        /// 1125        /// * recipient: Address of token recipient.1126        /// 1127        /// * collection_id.1128        /// 1129        /// * item_id: ID of the item.1130        /// 1131        /// * value: Amount to transfer.1132        #[weight = <T as Config>::WeightInfo::transfer_from()]1133        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11341135            let sender = ensure_signed(origin)?;1136            let mut appoved_transfer = false;11371138            // Check approval1139            let mut approval: u128 = 0;1140            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1141                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1142                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1143                appoved_transfer = true;1144            }11451146            let target_collection = <Collection<T>>::get(collection_id);11471148            // Limits check1149            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11501151            // Transfer permissions check         1152            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1153                Error::<T>::NoPermission);11541155            if target_collection.access == AccessMode::WhiteList {1156                Self::check_white_list(collection_id, &sender)?;1157                Self::check_white_list(collection_id, &recipient)?;1158            }11591160            // Reduce approval by transferred amount or remove if remaining approval drops to 01161            if approval.checked_sub(value).unwrap_or(0) > 0 {1162                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1163            }1164            else {1165                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1166            }11671168            match target_collection.mode1169            {1170                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1171                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1172                CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1173                _ => ()1174            };11751176            Ok(())1177        }11781179        // #[weight = 0]1180        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11811182        //     // let no_perm_mes = "You do not have permissions to modify this collection";1183        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1184        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1185        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11861187        //     // // on_nft_received  call11881189        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;11901191        //     Ok(())1192        // }11931194        /// Set off-chain data schema.1195        /// 1196        /// # Permissions1197        /// 1198        /// * Collection Owner1199        /// * Collection Admin1200        /// 1201        /// # Arguments1202        /// 1203        /// * collection_id.1204        /// 1205        /// * schema: String representing the offchain data schema.1206        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1207        pub fn set_variable_meta_data (1208            origin,1209            collection_id: CollectionId,1210            item_id: TokenId,1211            data: Vec<u8>1212        ) -> DispatchResult {1213            let sender = ensure_signed(origin)?;1214            1215            Self::collection_exists(collection_id)?;1216            Self::token_exists(collection_id, item_id, &sender)?;12171218            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12191220            // Modify permissions check1221            let target_collection = <Collection<T>>::get(collection_id);1222            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1223                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1224                Error::<T>::NoPermission);12251226            match target_collection.mode1227            {1228                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1229                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1230                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1231                _ => fail!(Error::<T>::UnexpectedCollectionType)1232            };12331234            Ok(())1235        }1236 1237        /// Set schema standard1238        /// ImageURL1239        /// Unique1240        /// 1241        /// # Permissions1242        /// 1243        /// * Collection Owner1244        /// * Collection Admin1245        /// 1246        /// # Arguments1247        /// 1248        /// * collection_id.1249        /// 1250        /// * schema: SchemaVersion: enum1251        #[weight = <T as Config>::WeightInfo::set_schema_version()]1252        pub fn set_schema_version(1253            origin,1254            collection_id: CollectionId,1255            version: SchemaVersion1256        ) -> DispatchResult {1257            let sender = ensure_signed(origin)?;1258            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1259            let mut target_collection = <Collection<T>>::get(collection_id);1260            target_collection.schema_version = version;1261            <Collection<T>>::insert(collection_id, target_collection);12621263            Ok(())1264        }12651266        /// Set off-chain data schema.1267        /// 1268        /// # Permissions1269        /// 1270        /// * Collection Owner1271        /// * Collection Admin1272        /// 1273        /// # Arguments1274        /// 1275        /// * collection_id.1276        /// 1277        /// * schema: String representing the offchain data schema.1278        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1279        pub fn set_offchain_schema(1280            origin,1281            collection_id: CollectionId,1282            schema: Vec<u8>1283        ) -> DispatchResult {1284            let sender = ensure_signed(origin)?;1285            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12861287            // check schema limit1288            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");12891290            let mut target_collection = <Collection<T>>::get(collection_id);1291            target_collection.offchain_schema = schema;1292            <Collection<T>>::insert(collection_id, target_collection);12931294            Ok(())1295        }12961297        /// Set const on-chain data schema.1298        /// 1299        /// # Permissions1300        /// 1301        /// * Collection Owner1302        /// * Collection Admin1303        /// 1304        /// # Arguments1305        /// 1306        /// * collection_id.1307        /// 1308        /// * schema: String representing the const on-chain data schema.1309        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1310        pub fn set_const_on_chain_schema (1311            origin,1312            collection_id: CollectionId,1313            schema: Vec<u8>1314        ) -> DispatchResult {1315            let sender = ensure_signed(origin)?;1316            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13171318            // check schema limit1319            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");13201321            let mut target_collection = <Collection<T>>::get(collection_id);1322            target_collection.const_on_chain_schema = schema;1323            <Collection<T>>::insert(collection_id, target_collection);13241325            Ok(())1326        }13271328        /// Set variable on-chain data schema.1329        /// 1330        /// # Permissions1331        /// 1332        /// * Collection Owner1333        /// * Collection Admin1334        /// 1335        /// # Arguments1336        /// 1337        /// * collection_id.1338        /// 1339        /// * schema: String representing the variable on-chain data schema.1340        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1341        pub fn set_variable_on_chain_schema (1342            origin,1343            collection_id: CollectionId,1344            schema: Vec<u8>1345        ) -> DispatchResult {1346            let sender = ensure_signed(origin)?;1347            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13481349            // check schema limit1350            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");13511352            let mut target_collection = <Collection<T>>::get(collection_id);1353            target_collection.variable_on_chain_schema = schema;1354            <Collection<T>>::insert(collection_id, target_collection);13551356            Ok(())1357        }13581359        // Sudo permissions function1360        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1361        pub fn set_chain_limits(1362            origin,1363            limits: ChainLimits1364        ) -> DispatchResult {13651366            #[cfg(not(feature = "runtime-benchmarks"))]1367            ensure_root(origin)?;13681369            <ChainLimit>::put(limits);1370            Ok(())1371        }13721373        /// Enable smart contract self-sponsoring.1374        /// 1375        /// # Permissions1376        /// 1377        /// * Contract Owner1378        /// 1379        /// # Arguments1380        /// 1381        /// * contract address1382        /// * enable flag1383        /// 1384        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1385        pub fn enable_contract_sponsoring(1386            origin,1387            contract_address: T::AccountId,1388            enable: bool1389        ) -> DispatchResult {13901391            let sender = ensure_signed(origin)?;13921393            #[cfg(feature = "runtime-benchmarks")]1394            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13951396            Self::ensure_contract_owned(sender, &contract_address)?;13971398            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1399            Ok(())1400        }14011402        /// Set the rate limit for contract sponsoring to specified number of blocks.1403        /// 1404        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1405        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1406        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1407        /// from contract endowment if there are at least B blocks between such transactions. 1408        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1409        /// 1410        /// # Permissions1411        /// 1412        /// * Contract Owner1413        /// 1414        /// # Arguments1415        /// 1416        /// -`contract_address`: Address of the contract to sponsor1417        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1418        /// 1419        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1420        pub fn set_contract_sponsoring_rate_limit(1421            origin,1422            contract_address: T::AccountId,1423            rate_limit: T::BlockNumber1424        ) -> DispatchResult {1425            let sender = ensure_signed(origin)?;14261427            #[cfg(feature = "runtime-benchmarks")]1428            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14291430            Self::ensure_contract_owned(sender, &contract_address)?;1431            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1432            Ok(())1433        }14341435        #[weight = <T as Config>::WeightInfo::set_variable_meta_data_sponsoring_rate_limit()]1436        pub fn set_variable_meta_data_sponsoring_rate_limit(1437            origin,1438            collection_id: CollectionId,1439            rate_limit: T::BlockNumber1440        ) -> DispatchResult {1441            let sender = ensure_signed(origin)?;1442            Self::check_collection_sponsor(collection_id.clone(), sender.clone())?;14431444            <VariableMetaDataSponsoringRateLimit<T>>::insert(collection_id, rate_limit);1445            Ok(())1446        }14471448        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1449        /// 1450        /// # Permissions1451        /// 1452        /// * Address that deployed smart contract.1453        /// 1454        /// # Arguments1455        /// 1456        /// -`contract_address`: Address of the contract.1457        /// 1458        /// - `enable`: .  1459        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1460        pub fn toggle_contract_white_list(1461            origin,1462            contract_address: T::AccountId,1463            enable: bool1464        ) -> DispatchResult {1465            let sender = ensure_signed(origin)?;14661467            #[cfg(feature = "runtime-benchmarks")]1468            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14691470            Self::ensure_contract_owned(sender, &contract_address)?;1471            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1472            Ok(())1473        }1474        1475        /// Add an address to smart contract white list.1476        /// 1477        /// # Permissions1478        /// 1479        /// * Address that deployed smart contract.1480        /// 1481        /// # Arguments1482        /// 1483        /// -`contract_address`: Address of the contract.1484        ///1485        /// -`account_address`: Address to add.1486        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1487        pub fn add_to_contract_white_list(1488            origin,1489            contract_address: T::AccountId,1490            account_address: T::AccountId1491        ) -> DispatchResult {1492            let sender = ensure_signed(origin)?;14931494            #[cfg(feature = "runtime-benchmarks")]1495            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1496            1497            Self::ensure_contract_owned(sender, &contract_address)?;      1498            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1499            Ok(())1500        }15011502        /// Remove an address from smart contract white list.1503        /// 1504        /// # Permissions1505        /// 1506        /// * Address that deployed smart contract.1507        /// 1508        /// # Arguments1509        /// 1510        /// -`contract_address`: Address of the contract.1511        ///1512        /// -`account_address`: Address to remove.1513        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1514        pub fn remove_from_contract_white_list(1515            origin,1516            contract_address: T::AccountId,1517            account_address: T::AccountId1518        ) -> DispatchResult {1519            let sender = ensure_signed(origin)?;15201521            #[cfg(feature = "runtime-benchmarks")]1522            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15231524            Self::ensure_contract_owned(sender, &contract_address)?;1525            <ContractWhiteList<T>>::remove(contract_address, account_address);1526            Ok(())1527        }15281529        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1530        pub fn set_collection_limits(1531            origin,1532            collection_id: u32,1533            limits: CollectionLimits,1534        ) -> DispatchResult {1535            let sender = ensure_signed(origin)?;1536            Self::check_owner_permissions(collection_id, sender.clone())?;1537            let mut target_collection = <Collection<T>>::get(collection_id);1538            let chain_limits = ChainLimit::get();1539            let climits = target_collection.limits;15401541            // collection bounds1542            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1543                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1544                Error::<T>::CollectionLimitBoundsExceeded);15451546            // token_limit   check  prev1547            ensure!(climits.token_limit > limits.token_limit && 1548                limits.token_limit <= chain_limits.account_token_ownership_limit, 1549                Error::<T>::AccountTokenLimitExceeded);15501551            target_collection.limits = limits;1552            <Collection<T>>::insert(collection_id, target_collection);15531554            Ok(())1555        } 1556    }1557}15581559impl<T: Config> Module<T> {15601561    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {15621563        let target_collection = <Collection<T>>::get(collection_id);15641565        // Limits check1566        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;15671568        // Transfer permissions check1569        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1570            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1571            Error::<T>::NoPermission);15721573        if target_collection.access == AccessMode::WhiteList {1574            Self::check_white_list(collection_id, &sender)?;1575            Self::check_white_list(collection_id, &recipient)?;1576        }15771578        match target_collection.mode1579        {1580            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1581            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1582            CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1583            _ => ()1584        };15851586        Ok(())1587    }158815891590    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15911592        // check token limit and account token limit1593        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1594        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1595        1596        Ok(())1597    }15981599    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {16001601        // check token limit and account token limit1602        let total_items: u32 = ItemListIndex::get(collection_id);1603        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1604        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1605        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);16061607        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1608            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1609            Self::check_white_list(collection_id, owner)?;1610            Self::check_white_list(collection_id, sender)?;1611        }16121613        Ok(())1614    }16151616    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1617        match target_collection.mode1618        {1619            CollectionMode::NFT => {1620                if let CreateItemData::NFT(data) = data {1621                    // check sizes1622                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1623                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1624                } else {1625                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1626                }1627            },1628            CollectionMode::Fungible(_) => {1629                if let CreateItemData::Fungible(_) = data {1630                } else {1631                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1632                }1633            },1634            CollectionMode::ReFungible => {1635                if let CreateItemData::ReFungible(data) = data {16361637                    // check sizes1638                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1639                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16401641                    // Check refungibility limits1642                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1643                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1644                } else {1645                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1646                }1647            },1648            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1649        };16501651        Ok(())1652    }16531654    fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1655        match data1656        {1657            CreateItemData::NFT(data) => {1658                let item = NftItemType {1659                    owner,1660                    const_data: data.const_data,1661                    variable_data: data.variable_data1662                };16631664                Self::add_nft_item(collection_id, item)?;1665            },1666            CreateItemData::Fungible(data) => {1667                Self::add_fungible_item(collection_id, &owner, data.value)?;1668            },1669            CreateItemData::ReFungible(data) => {1670                let mut owner_list = Vec::new();1671                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});16721673                let item = ReFungibleItemType {1674                    owner: owner_list,1675                    const_data: data.const_data,1676                    variable_data: data.variable_data1677                };16781679                Self::add_refungible_item(collection_id, item)?;1680            }1681        };16821683        // call event1684        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16851686        Ok(())1687    }16881689    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {16901691        // Does new owner already have an account?1692        let mut balance: u128 = 0;1693        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1694            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1695        } 16961697        // Mint 1698        let item = FungibleItemType {1699            value: balance + value1700        };1701        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);17021703        // Update balance1704        let new_balance = <Balance<T>>::get(collection_id, owner)1705            .checked_add(value)1706            .ok_or(Error::<T>::NumOverflow)?;1707        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17081709        Ok(())1710    }17111712    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1713        let current_index = <ItemListIndex>::get(collection_id)1714            .checked_add(1)1715            .ok_or(Error::<T>::NumOverflow)?;1716        let itemcopy = item.clone();17171718        let value = item.owner.first().unwrap().fraction;1719        let owner = item.owner.first().unwrap().owner.clone();17201721        Self::add_token_index(collection_id, current_index, &owner)?;17221723        <ItemListIndex>::insert(collection_id, current_index);1724        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17251726        // Update balance1727        let new_balance = <Balance<T>>::get(collection_id, &owner)1728            .checked_add(value)1729            .ok_or(Error::<T>::NumOverflow)?;1730        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17311732        Ok(())1733    }17341735    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1736        let current_index = <ItemListIndex>::get(collection_id)1737            .checked_add(1)1738            .ok_or(Error::<T>::NumOverflow)?;17391740        let item_owner = item.owner.clone();1741        Self::add_token_index(collection_id, current_index, &item.owner)?;17421743        <ItemListIndex>::insert(collection_id, current_index);1744        <NftItemList<T>>::insert(collection_id, current_index, item);17451746        // Update balance1747        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1748            .checked_add(1)1749            .ok_or(Error::<T>::NumOverflow)?;1750        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17511752        Ok(())1753    }17541755    fn burn_refungible_item(1756        collection_id: CollectionId,1757        item_id: TokenId,1758        owner: &T::AccountId,1759    ) -> DispatchResult {1760        ensure!(1761            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1762            Error::<T>::TokenNotFound1763        );1764        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1765        let rft_balance = token1766            .owner1767            .iter()1768            .filter(|&i| i.owner == *owner)1769            .next()1770            .unwrap();1771        Self::remove_token_index(collection_id, item_id, owner)?;17721773        // update balance1774        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1775            .checked_sub(rft_balance.fraction)1776            .ok_or(Error::<T>::NumOverflow)?;1777        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);17781779        // Re-create owners list with sender removed1780        let index = token1781            .owner1782            .iter()1783            .position(|i| i.owner == *owner)1784            .unwrap();1785        token.owner.remove(index);1786        let owner_count = token.owner.len();17871788        // Burn the token completely if this was the last (only) owner1789        if owner_count == 0 {1790            <ReFungibleItemList<T>>::remove(collection_id, item_id);1791            <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1792        }1793        else {1794            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1795        }17961797        Ok(())1798    }17991800    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1801        ensure!(1802            <NftItemList<T>>::contains_key(collection_id, item_id),1803            Error::<T>::TokenNotFound1804        );1805        let item = <NftItemList<T>>::get(collection_id, item_id);1806        Self::remove_token_index(collection_id, item_id, &item.owner)?;18071808        // update balance1809        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1810            .checked_sub(1)1811            .ok_or(Error::<T>::NumOverflow)?;1812        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1813        <NftItemList<T>>::remove(collection_id, item_id);1814        <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18151816        Ok(())1817    }18181819    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1820        ensure!(1821            <FungibleItemList<T>>::contains_key(collection_id, owner),1822            Error::<T>::TokenNotFound1823        );1824        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1825        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18261827        // update balance1828        let new_balance = <Balance<T>>::get(collection_id, owner)1829            .checked_sub(value)1830            .ok_or(Error::<T>::NumOverflow)?;1831        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18321833        if balance.value - value > 0 {1834            balance.value -= value;1835            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1836        }1837        else {1838            <FungibleItemList<T>>::remove(collection_id, owner);1839        }18401841        Ok(())1842    }18431844    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1845        ensure!(1846            <Collection<T>>::contains_key(collection_id),1847            Error::<T>::CollectionNotFound1848        );1849        Ok(())1850    }18511852    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1853        Self::collection_exists(collection_id)?;18541855        let target_collection = <Collection<T>>::get(collection_id);1856        ensure!(1857            subject == target_collection.owner,1858            Error::<T>::NoPermission1859        );18601861        Ok(())1862    }18631864    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1865        let target_collection = <Collection<T>>::get(collection_id);1866        let mut result: bool = subject == target_collection.owner;1867        let exists = <AdminList<T>>::contains_key(collection_id);18681869        if !result & exists {1870            if <AdminList<T>>::get(collection_id).contains(&subject) {1871                result = true1872            }1873        }18741875        result1876    }18771878    fn check_owner_or_admin_permissions(1879        collection_id: CollectionId,1880        subject: T::AccountId,1881    ) -> DispatchResult {1882        Self::collection_exists(collection_id)?;1883        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18841885        ensure!(1886            result,1887            Error::<T>::NoPermission1888        );1889        Ok(())1890    }18911892    fn check_collection_sponsor(1893        collection_id: CollectionId,1894        subject: T::AccountId,1895    ) -> DispatchResult {1896        Self::collection_exists(collection_id)?;1897        let collection = <Collection<T>>::get(collection_id);1898        1899        ensure!(1900            collection.sponsor_confirmed &&1901            collection.sponsor == subject,1902            Error::<T>::NoPermission,1903        );1904        Ok(())1905    }19061907    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1908        let target_collection = <Collection<T>>::get(collection_id);19091910        match target_collection.mode {1911            CollectionMode::NFT => {1912                <NftItemList<T>>::get(collection_id, item_id).owner == subject1913            }1914            CollectionMode::Fungible(_) => {1915                <FungibleItemList<T>>::contains_key(collection_id, &subject)1916            }1917            CollectionMode::ReFungible => {1918                <ReFungibleItemList<T>>::get(collection_id, item_id)1919                    .owner1920                    .iter()1921                    .any(|i| i.owner == subject)1922            }1923            CollectionMode::Invalid => false,1924        }1925    }19261927    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1928        let mes = Error::<T>::AddresNotInWhiteList;1929        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);19301931        Ok(())1932    }19331934    /// Check if token exists. In case of Fungible, check if there is an entry for 1935    /// the owner in fungible balances double map1936    fn token_exists(1937        collection_id: CollectionId,1938        item_id: TokenId,1939        owner: &T::AccountId1940    ) -> DispatchResult {1941        let target_collection = <Collection<T>>::get(collection_id);1942        let exists = match target_collection.mode1943        {1944            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1945            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),1946            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1947            _ => false1948        };19491950        ensure!(exists == true, Error::<T>::TokenNotFound);1951        Ok(())1952    }19531954    fn transfer_fungible(1955        collection_id: CollectionId,1956        value: u128,1957        owner: &T::AccountId,1958        recipient: &T::AccountId,1959    ) -> DispatchResult {1960        Self::token_exists(collection_id, 0, owner)?;19611962        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1963        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19641965        // Send balance to recipient (updates balanceOf of recipient)1966        Self::add_fungible_item(collection_id, recipient, value)?;19671968        // update balanceOf of sender1969        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);19701971        // Reduce or remove sender1972        if balance.value == value {1973            <FungibleItemList<T>>::remove(collection_id, owner);1974        }1975        else {1976            balance.value -= value;1977            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1978        }19791980        Ok(())1981    }19821983    fn transfer_refungible(1984        collection_id: CollectionId,1985        item_id: TokenId,1986        value: u128,1987        owner: T::AccountId,1988        new_owner: T::AccountId,1989    ) -> DispatchResult {1990        Self::token_exists(collection_id, item_id, &owner)?;19911992        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1993        let item = full_item1994            .owner1995            .iter()1996            .filter(|i| i.owner == owner)1997            .next()1998            .ok_or(Error::<T>::NumOverflow)?;1999        let amount = item.fraction;20002001        ensure!(amount >= value, Error::<T>::TokenValueTooLow);20022003        // update balance2004        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2005            .checked_sub(value)2006            .ok_or(Error::<T>::NumOverflow)?;2007        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20082009        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2010            .checked_add(value)2011            .ok_or(Error::<T>::NumOverflow)?;2012        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20132014        let old_owner = item.owner.clone();2015        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20162017        // transfer2018        if amount == value && !new_owner_has_account {2019            // change owner2020            // new owner do not have account2021            let mut new_full_item = full_item.clone();2022            new_full_item2023                .owner2024                .iter_mut()2025                .find(|i| i.owner == owner)2026                .unwrap()2027                .owner = new_owner.clone();2028            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20292030            // update index collection2031            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2032        } else {2033            let mut new_full_item = full_item.clone();2034            new_full_item2035                .owner2036                .iter_mut()2037                .find(|i| i.owner == owner)2038                .unwrap()2039                .fraction -= value;20402041            // separate amount2042            if new_owner_has_account {2043                // new owner has account2044                new_full_item2045                    .owner2046                    .iter_mut()2047                    .find(|i| i.owner == new_owner)2048                    .unwrap()2049                    .fraction += value;2050            } else {2051                // new owner do not have account2052                new_full_item.owner.push(Ownership {2053                    owner: new_owner.clone(),2054                    fraction: value,2055                });2056                Self::add_token_index(collection_id, item_id, &new_owner)?;2057            }20582059            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2060        }20612062        Ok(())2063    }20642065    fn transfer_nft(2066        collection_id: CollectionId,2067        item_id: TokenId,2068        sender: T::AccountId,2069        new_owner: T::AccountId,2070    ) -> DispatchResult {2071        Self::token_exists(collection_id, item_id, &sender)?;20722073        let mut item = <NftItemList<T>>::get(collection_id, item_id);20742075        ensure!(2076            sender == item.owner,2077            Error::<T>::MustBeTokenOwner2078        );20792080        // update balance2081        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2082            .checked_sub(1)2083            .ok_or(Error::<T>::NumOverflow)?;2084        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20852086        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2087            .checked_add(1)2088            .ok_or(Error::<T>::NumOverflow)?;2089        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20902091        // change owner2092        let old_owner = item.owner.clone();2093        item.owner = new_owner.clone();2094        <NftItemList<T>>::insert(collection_id, item_id, item);20952096        // update index collection2097        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;20982099        Ok(())2100    }2101    2102    fn set_re_fungible_variable_data(2103        collection_id: CollectionId,2104        item_id: TokenId,2105        data: Vec<u8>2106    ) -> DispatchResult {2107        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);21082109        item.variable_data = data;21102111        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21122113        Ok(())2114    }21152116    fn set_nft_variable_data(2117        collection_id: CollectionId,2118        item_id: TokenId,2119        data: Vec<u8>2120    ) -> DispatchResult {2121        let mut item = <NftItemList<T>>::get(collection_id, item_id);2122        2123        item.variable_data = data;21242125        <NftItemList<T>>::insert(collection_id, item_id, item);2126        2127        Ok(())2128    }21292130    fn init_collection(item: &CollectionType<T::AccountId>) {2131        // check params2132        assert!(2133            item.decimal_points <= MAX_DECIMAL_POINTS,2134            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2135        );2136        assert!(2137            item.name.len() <= 64,2138            "Collection name can not be longer than 63 char"2139        );2140        assert!(2141            item.name.len() <= 256,2142            "Collection description can not be longer than 255 char"2143        );2144        assert!(2145            item.token_prefix.len() <= 16,2146            "Token prefix can not be longer than 15 char"2147        );21482149        // Generate next collection ID2150        let next_id = CreatedCollectionCount::get()2151            .checked_add(1)2152            .unwrap();21532154        CreatedCollectionCount::put(next_id);2155    }21562157    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2158        let current_index = <ItemListIndex>::get(collection_id)2159            .checked_add(1)2160            .unwrap();21612162        let item_owner = item.owner.clone();2163        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21642165        <ItemListIndex>::insert(collection_id, current_index);21662167        // Update balance2168        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2169            .checked_add(1)2170            .unwrap();2171        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2172    }21732174    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2175        let current_index = <ItemListIndex>::get(collection_id)2176            .checked_add(1)2177            .unwrap();21782179        Self::add_token_index(collection_id, current_index, owner).unwrap();21802181        <ItemListIndex>::insert(collection_id, current_index);21822183        // Update balance2184        let new_balance = <Balance<T>>::get(collection_id, owner)2185            .checked_add(item.value)2186            .unwrap();2187        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2188    }21892190    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2191        let current_index = <ItemListIndex>::get(collection_id)2192            .checked_add(1)2193            .unwrap();21942195        let value = item.owner.first().unwrap().fraction;2196        let owner = item.owner.first().unwrap().owner.clone();21972198        Self::add_token_index(collection_id, current_index, &owner).unwrap();21992200        <ItemListIndex>::insert(collection_id, current_index);22012202        // Update balance2203        let new_balance = <Balance<T>>::get(collection_id, &owner)2204            .checked_add(value)2205            .unwrap();2206        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2207    }22082209    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {22102211        // add to account limit2212        if <AccountItemCount<T>>::contains_key(owner) {22132214            // bound Owned tokens by a single address2215            let count = <AccountItemCount<T>>::get(owner);2216            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);22172218            <AccountItemCount<T>>::insert(owner.clone(), count2219                .checked_add(1)2220                .ok_or(Error::<T>::NumOverflow)?);2221        }2222        else {2223            <AccountItemCount<T>>::insert(owner.clone(), 1);2224        }22252226        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2227        if list_exists {2228            let mut list = <AddressTokens<T>>::get(collection_id, owner);2229            let item_contains = list.contains(&item_index.clone());22302231            if !item_contains {2232                list.push(item_index.clone());2233            }22342235            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2236        } else {2237            let mut itm = Vec::new();2238            itm.push(item_index.clone());2239            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2240        }22412242        Ok(())2243    }22442245    fn remove_token_index(2246        collection_id: CollectionId,2247        item_index: TokenId,2248        owner: &T::AccountId,2249    ) -> DispatchResult {22502251        // update counter2252        <AccountItemCount<T>>::insert(owner.clone(), 2253            <AccountItemCount<T>>::get(owner)2254            .checked_sub(1)2255            .ok_or(Error::<T>::NumOverflow)?);225622572258        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2259        if list_exists {2260            let mut list = <AddressTokens<T>>::get(collection_id, owner);2261            let item_contains = list.contains(&item_index.clone());22622263            if item_contains {2264                list.retain(|&item| item != item_index);2265                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2266            }2267        }22682269        Ok(())2270    }22712272    fn move_token_index(2273        collection_id: CollectionId,2274        item_index: TokenId,2275        old_owner: &T::AccountId,2276        new_owner: &T::AccountId,2277    ) -> DispatchResult {2278        Self::remove_token_index(collection_id, item_index, old_owner)?;2279        Self::add_token_index(collection_id, item_index, new_owner)?;22802281        Ok(())2282    }2283    2284    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2285        if <ContractOwner<T>>::contains_key(contract.clone()) {2286            let owner = <ContractOwner<T>>::get(contract);2287            ensure!(account == owner, Error::<T>::NoPermission);2288        } else {2289            fail!(Error::<T>::NoPermission);2290        }22912292        Ok(())2293    }2294}22952296////////////////////////////////////////////////////////////////////////////////////////////////////2297// Economic models2298// #region22992300/// Fee multiplier.2301pub type Multiplier = FixedU128;23022303type BalanceOf<T> = <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::Balance;23042305/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2306/// in the queue.2307#[derive(Encode, Decode, Clone, Eq, PartialEq)]2308pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);23092310impl<T: Config + Send + Sync> sp_std::fmt::Debug 2311    for ChargeTransactionPayment<T>2312{2313	#[cfg(feature = "std")]2314	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2315		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2316	}2317	#[cfg(not(feature = "std"))]2318	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2319		Ok(())2320	}2321}23222323impl<T: Config> ChargeTransactionPayment<T>2324where2325    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2326    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2327    T::AccountId: AsRef<[u8]>,2328    T::AccountId: UncheckedFrom<T::Hash>,2329{2330    fn traditional_fee(2331        len: usize,2332        info: &DispatchInfoOf<T::Call>,2333        tip: BalanceOf<T>,2334    ) -> BalanceOf<T>2335    where2336        T::Call: Dispatchable<Info = DispatchInfo>,2337    {2338        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2339    }23402341	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2342        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2343        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2344        let len_saturation = max_block_length as u64 / (len as u64).max(1);2345        let coefficient: BalanceOf<T> = weight_saturation2346            .min(len_saturation)2347            .saturated_into::<BalanceOf<T>>();2348        final_fee2349            .saturating_mul(coefficient)2350            .saturated_into::<TransactionPriority>()2351    }23522353    fn withdraw_fee(2354        &self,2355        who: &T::AccountId,2356        call: &T::Call,2357        info: &DispatchInfoOf<T::Call>,2358        len: usize,2359	) -> Result<2360		(2361			BalanceOf<T>,2362			<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2363		),2364		TransactionValidityError,2365	> {2366        let tip = self.0;23672368        // Set fee based on call type. Creating collection costs 1 Unique.2369        // All other transactions have traditional fees so far2370        // let fee = match call.is_sub_type() {2371        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2372        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2373        //                                                 // _ => <BalanceOf<T>>::from(100)2374        // };2375        let fee = Self::traditional_fee(len, info, tip);23762377        // Only mess with balances if fee is not zero.2378        if fee.is_zero() {2379            return <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2380			.map(|i| (fee, i));2381        }23822383        // Determine who is paying transaction fee based on ecnomic model2384        // Parse call to extract collection ID and access collection sponsor2385        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2386            Some(Call::create_item(collection_id, _owner, _properties)) => {23872388                // sponsor timeout2389                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;23902391                let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;2392                let mut sponsored = true;2393                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2394                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2395                    let limit_time = last_tx_block + limit.into();2396                    if block_number <= limit_time {2397                        sponsored = false;2398                    }2399                }2400                if sponsored {2401                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2402                }24032404                // check free create limit2405                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2406                   (<Collection<T>>::get(collection_id).sponsor_confirmed) &&2407                   (sponsored)2408                {2409                    <Collection<T>>::get(collection_id).sponsor2410                } else {2411                    T::AccountId::default()2412                }2413            }2414            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2415                2416                let mut sponsor_transfer = false;2417                if <Collection<T>>::get(collection_id).sponsor_confirmed {24182419                    let collection_limits = <Collection<T>>::get(collection_id).limits;2420                    let collection_mode = <Collection<T>>::get(collection_id).mode;2421    2422                    // sponsor timeout2423                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2424                    sponsor_transfer = match collection_mode {2425                        CollectionMode::NFT => {2426    2427                            // get correct limit2428                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2429                                collection_limits.sponsor_transfer_timeout2430                            } else {2431                                ChainLimit::get().nft_sponsor_transfer_timeout2432                            };2433    2434                            let mut sponsored = true;2435                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2436                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2437                                let limit_time = last_tx_block + limit.into();2438                                if block_number <= limit_time {2439                                    sponsored = false;2440                                }2441                            }2442                            if sponsored {2443                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2444                            }24452446                            sponsored2447                        }2448                        CollectionMode::Fungible(_) => {2449    2450                            // get correct limit2451                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2452                                collection_limits.sponsor_transfer_timeout2453                            } else {2454                                ChainLimit::get().fungible_sponsor_transfer_timeout2455                            };2456    2457                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2458                            let mut sponsored = true;2459                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2460                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2461                                let limit_time = last_tx_block + limit.into();2462                                if block_number <= limit_time {2463                                    sponsored = false;2464                                }2465                            }2466                            if sponsored {2467                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2468                            }24692470                            sponsored2471                        }2472                        CollectionMode::ReFungible => {2473    2474                            // get correct limit2475                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2476                                collection_limits.sponsor_transfer_timeout2477                            } else {2478                                ChainLimit::get().refungible_sponsor_transfer_timeout2479                            };2480    2481                            let mut sponsored = true;2482                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2483                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2484                                let limit_time = last_tx_block + limit.into();2485                                if block_number <= limit_time {2486                                    sponsored = false;2487                                }2488                            }2489                            if sponsored {2490                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2491                            }24922493                            sponsored2494                        }2495                        _ => {2496                            false2497                        },2498                    };2499                }25002501                if !sponsor_transfer {2502                    T::AccountId::default()2503                } else {2504                    <Collection<T>>::get(collection_id).sponsor2505                }2506            }25072508            Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2509                let mut sponsor_metadata_changes = false;25102511                let collection = <Collection<T>>::get(collection_id);2512                if2513                    collection.sponsor_confirmed &&2514                    // Can't sponsor fungible collection, this tx will be rejected2515                    // as invalid2516                    !matches!(collection.mode, CollectionMode::Fungible(_)) &&2517                    data.len() <= collection.limits.sponsored_data_size as usize2518                {2519                    let rate_limit = <VariableMetaDataSponsoringRateLimit<T>>::get(collection_id);2520                    if rate_limit > 0.into() {2521                        // sponsor timeout2522                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;25232524                        if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2525                            .map(|last_block| block_number - last_block >= rate_limit)2526                            .unwrap_or(true) 2527                        {2528                            sponsor_metadata_changes = true;2529                            <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2530                        }2531                    }2532                }25332534                if !sponsor_metadata_changes {2535                    T::AccountId::default()2536                } else {2537                    <Collection<T>>::get(collection_id).sponsor2538                }2539            }25402541            _ => T::AccountId::default(),2542        };25432544        // Sponsor smart contracts2545        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {25462547            // On instantiation: set the contract owner2548            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {25492550                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2551                    &who,2552                    code_hash,2553                    salt,2554                );2555                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());25562557                T::AccountId::default()2558            },25592560            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2561            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {25622563                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());25642565                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2566                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2567                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2568                  2569                if !owned_contract && white_list_enabled {2570                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2571                        return Err(InvalidTransaction::Call.into());2572                    }2573                }25742575                let mut sponsor_transfer = false;2576                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2577                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2578                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2579                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2580                    let limit_time = last_tx_block + rate_limit;25812582                    if block_number >= limit_time {2583                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2584                        sponsor_transfer = true;2585                    }2586                } else {2587                    sponsor_transfer = false;2588                }2589               2590                2591                let mut sp = T::AccountId::default();2592                if sponsor_transfer {2593                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2594                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2595                            sp = called_contract;2596                        }2597                    }2598                }25992600                sp2601            },26022603            _ => sponsor,2604        };26052606        let mut who_pays_fee: T::AccountId = sponsor.clone();2607        if sponsor == T::AccountId::default() {2608            who_pays_fee = who.clone();2609        }26102611		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2612			.map(|i| (fee, i))2613    }2614}261526162617impl<T: Config + Send + Sync> SignedExtension2618    for ChargeTransactionPayment<T>2619where2620    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2621    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2622    T::AccountId: AsRef<[u8]>,2623    T::AccountId: UncheckedFrom<T::Hash>,2624{2625    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2626    type AccountId = T::AccountId;2627    type Call = T::Call;2628    type AdditionalSigned = ();2629    type Pre = (2630        // tip2631        BalanceOf<T>,2632        // who pays fee2633        Self::AccountId,2634		// imbalance resulting from withdrawing the fee2635		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2636    );2637    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2638        Ok(())2639    }26402641    fn validate(2642        &self,2643        who: &Self::AccountId,2644        call: &Self::Call,2645        info: &DispatchInfoOf<Self::Call>,2646        len: usize,2647    ) -> TransactionValidity {2648		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2649		Ok(ValidTransaction {2650			priority: Self::get_priority(len, info, fee),2651			..Default::default()2652		})2653    }26542655    fn pre_dispatch(2656        self,2657        who: &Self::AccountId,2658        call: &Self::Call,2659        info: &DispatchInfoOf<Self::Call>,2660        len: usize,2661    ) -> Result<Self::Pre, TransactionValidityError> {2662        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2663        Ok((self.0, who.clone(), imbalance))2664    }26652666    fn post_dispatch(2667        pre: Self::Pre,2668        info: &DispatchInfoOf<Self::Call>,2669        post_info: &PostDispatchInfoOf<Self::Call>,2670        len: usize,2671        _result: &DispatchResult,2672    ) -> Result<(), TransactionValidityError> {2673		let (tip, who, imbalance) = pre;2674		let actual_fee = transaction_payment::Module::<T>::compute_actual_fee(2675			len as u32,2676			info,2677			post_info,2678			tip,2679		);2680		<T as transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2681		Ok(())2682    }2683}26842685// #endregion