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

difftreelog

source

pallets/nft/src/lib.rs97.5 KiBsourcehistory
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, IsSubType,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial, DispatchClass,29    },30    StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use sp_runtime::traits::StaticLookup;45use pallet_contracts::chain_extension::UncheckedFrom;46use transaction_payment::OnChargeTransaction;4748#[cfg(test)]49mod mock;5051#[cfg(test)]52mod tests;5354mod default_weights;5556pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;57pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;58pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;59pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6061// Structs62// #region6364pub type CollectionId = u32;65pub type TokenId = u32;66pub type DecimalPoints = u8;6768#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]69#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]70pub enum CollectionMode {71    Invalid,72    NFT,73    // decimal points74    Fungible(DecimalPoints),75    ReFungible,76}7778impl Default for CollectionMode {79    fn default() -> Self {80        Self::Invalid81    }82}8384impl Into<u8> for CollectionMode {85    fn into(self) -> u8 {86        match self {87            CollectionMode::Invalid => 0,88            CollectionMode::NFT => 1,89            CollectionMode::Fungible(_) => 2,90            CollectionMode::ReFungible => 3,91        }92    }93}9495#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]96#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]97pub enum AccessMode {98    Normal,99    WhiteList,100}101impl Default for AccessMode {102    fn default() -> Self {103        Self::Normal104    }105}106107#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]108#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]109pub enum SchemaVersion {110    ImageURL,111    Unique,112}113impl Default for SchemaVersion {114    fn default() -> Self {115        Self::ImageURL116    }117}118119#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]120#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]121pub struct Ownership<AccountId> {122    pub owner: AccountId,123    pub fraction: u128,124}125126#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub struct CollectionType<AccountId> {129    pub owner: AccountId,130    pub mode: CollectionMode,131    pub access: AccessMode,132    pub decimal_points: DecimalPoints,133    pub name: Vec<u16>,        // 64 include null escape char134    pub description: Vec<u16>, // 256 include null escape char135    pub token_prefix: Vec<u8>, // 16 include null escape char136    pub mint_mode: bool,137    pub offchain_schema: Vec<u8>,138    pub schema_version: SchemaVersion,139    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender140    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.141    pub limits: CollectionLimits, // Collection private restrictions 142    pub variable_on_chain_schema: Vec<u8>, //143    pub const_on_chain_schema: Vec<u8>, //144}145146#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]147#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]148pub struct NftItemType<AccountId> {149    pub owner: AccountId,150    pub const_data: Vec<u8>,151    pub variable_data: Vec<u8>,152}153154#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]155#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]156pub struct FungibleItemType {157    pub value: u128,158}159160#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]161#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]162pub struct ReFungibleItemType<AccountId> {163    pub owner: Vec<Ownership<AccountId>>,164    pub const_data: Vec<u8>,165    pub variable_data: Vec<u8>,166}167168// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]169// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170// pub struct VestingItem<AccountId, Moment> {171//     pub sender: AccountId,172//     pub recipient: AccountId,173//     pub collection_id: CollectionId,174//     pub item_id: TokenId,175//     pub amount: u64,176//     pub vesting_date: Moment,177// }178179#[derive(Encode, Decode, Debug, Clone, PartialEq)]180#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]181pub struct CollectionLimits {182    pub account_token_ownership_limit: u32,183    pub sponsored_data_size: u32,184    pub token_limit: u32,185186    // Timeouts for item types in passed blocks187    pub sponsor_transfer_timeout: u32,188    pub owner_can_transfer: bool,189    pub owner_can_destroy: bool,190}191192impl Default for CollectionLimits {193    fn default() -> CollectionLimits {194        CollectionLimits { 195            account_token_ownership_limit: 10_000_000, 196            token_limit: u32::max_value(),197            sponsored_data_size: u32::max_value(), 198            sponsor_transfer_timeout: 14400,199            owner_can_transfer: true,200            owner_can_destroy: true201        }202    }203}204205#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]206#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]207pub struct ChainLimits {208    pub collection_numbers_limit: u32,209    pub account_token_ownership_limit: u32,210    pub collections_admins_limit: u64,211    pub custom_data_limit: u32,212213    // Timeouts for item types in passed blocks214    pub nft_sponsor_transfer_timeout: u32,215    pub fungible_sponsor_transfer_timeout: u32,216    pub refungible_sponsor_transfer_timeout: u32,217218    // Schema limits219    pub offchain_schema_limit: u32,220    pub variable_on_chain_schema_limit: u32,221    pub const_on_chain_schema_limit: u32,222}223224pub trait WeightInfo {225	fn create_collection() -> Weight;226	fn destroy_collection() -> Weight;227	fn add_to_white_list() -> Weight;228	fn remove_from_white_list() -> Weight;229    fn set_public_access_mode() -> Weight;230    fn set_mint_permission() -> Weight;231    fn change_collection_owner() -> Weight;232    fn add_collection_admin() -> Weight;233    fn remove_collection_admin() -> Weight;234    fn set_collection_sponsor() -> Weight;235    fn confirm_sponsorship() -> Weight;236    fn remove_collection_sponsor() -> Weight;237    fn create_item(s: usize) -> Weight;238    fn burn_item() -> Weight;239    fn transfer() -> Weight;240    fn approve() -> Weight;241    fn transfer_from() -> Weight;242    fn set_offchain_schema() -> Weight;243    fn set_const_on_chain_schema() -> Weight;244    fn set_variable_on_chain_schema() -> Weight;245    fn set_variable_meta_data() -> Weight;246    fn enable_contract_sponsoring() -> Weight;247    fn set_schema_version() -> Weight;248    fn set_chain_limits() -> Weight;249    fn set_contract_sponsoring_rate_limit() -> Weight;250    fn toggle_contract_white_list() -> Weight;251    fn add_to_contract_white_list() -> Weight;252    fn remove_from_contract_white_list() -> Weight;253    fn set_collection_limits() -> Weight;254}255256#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]257#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]258pub struct CreateNftData {259    pub const_data: Vec<u8>,260    pub variable_data: Vec<u8>,261}262263#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]264#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]265pub struct CreateFungibleData {266    pub value: u128,267}268269#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]270#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]271pub struct CreateReFungibleData {272    pub const_data: Vec<u8>,273    pub variable_data: Vec<u8>,274    pub pieces: u128,275}276277#[derive(Encode, Decode, Debug, Clone, PartialEq)]278#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]279pub enum CreateItemData {280    NFT(CreateNftData),281    Fungible(CreateFungibleData),282    ReFungible(CreateReFungibleData),283}284285impl CreateItemData {286    pub fn len(&self) -> usize {287        let len = match self {288            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),289            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),290            _ => 0291        };292        293        return len;294    }295}296297impl From<CreateNftData> for CreateItemData {298    fn from(item: CreateNftData) -> Self {299        CreateItemData::NFT(item)300    }301}302303impl From<CreateReFungibleData> for CreateItemData {304    fn from(item: CreateReFungibleData) -> Self {305        CreateItemData::ReFungible(item)306    }307}308309impl From<CreateFungibleData> for CreateItemData {310    fn from(item: CreateFungibleData) -> Self {311        CreateItemData::Fungible(item)312    }313}314315316decl_error! {317	/// Error for non-fungible-token module.318	pub enum Error for Module<T: Config> {319        /// Total collections bound exceeded.320        TotalCollectionsLimitExceeded,321		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.322        CollectionDecimalPointLimitExceeded, 323        /// Collection name can not be longer than 63 char.324        CollectionNameLimitExceeded, 325        /// Collection description can not be longer than 255 char.326        CollectionDescriptionLimitExceeded, 327        /// Token prefix can not be longer than 15 char.328        CollectionTokenPrefixLimitExceeded,329        /// This collection does not exist.330        CollectionNotFound,331        /// Item not exists.332        TokenNotFound,333        /// Admin not found334        AdminNotFound,335        /// Arithmetic calculation overflow.336        NumOverflow,       337        /// Account already has admin role.338        AlreadyAdmin,  339        /// You do not own this collection.340        NoPermission,341        /// This address is not set as sponsor, use setCollectionSponsor first.342        ConfirmUnsetSponsorFail,343        /// Collection is not in mint mode.344        PublicMintingNotAllowed,345        /// Sender parameter and item owner must be equal.346        MustBeTokenOwner,347        /// Item balance not enough.348        TokenValueTooLow,349        /// Size of item is too large.350        NftSizeLimitExceeded,351        /// No approve found352        ApproveNotFound,353        /// Requested value more than approved.354        TokenValueNotEnough,355        /// Only approved addresses can call this method.356        ApproveRequired,357        /// Address is not in white list.358        AddresNotInWhiteList,359        /// Number of collection admins bound exceeded.360        CollectionAdminsLimitExceeded,361        /// Owned tokens by a single address bound exceeded.362        AddressOwnershipLimitExceeded,363        /// Length of items properties must be greater than 0.364        EmptyArgument,365        /// const_data exceeded data limit.366        TokenConstDataLimitExceeded,367        /// variable_data exceeded data limit.368        TokenVariableDataLimitExceeded,369        /// Not NFT item data used to mint in NFT collection.370        NotNftDataUsedToMintNftCollectionToken,371        /// Not Fungible item data used to mint in Fungible collection.372        NotFungibleDataUsedToMintFungibleCollectionToken,373        /// Not Re Fungible item data used to mint in Re Fungible collection.374        NotReFungibleDataUsedToMintReFungibleCollectionToken,375        /// Unexpected collection type.376        UnexpectedCollectionType,377        /// Can't store metadata in fungible tokens.378        CantStoreMetadataInFungibleTokens,379        /// Collection token limit exceeded380        CollectionTokenLimitExceeded,381        /// Account token limit exceeded per collection382        AccountTokenLimitExceeded,383        /// Collection limit bounds per collection exceeded384        CollectionLimitBoundsExceeded,385        /// Schema data size limit bound exceeded386        SchemaDataLimitExceeded,387        /// Maximum refungibility exceeded388        WrongRefungiblePieces389	}390}391392pub trait Config: system::Config + Sized + transaction_payment::Config + pallet_contracts::Config {393    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;394395    /// Weight information for extrinsics in this pallet.396	type WeightInfo: WeightInfo;397}398399#[cfg(feature = "runtime-benchmarks")]400mod benchmarking;401402// #endregion403404decl_storage! {405    trait Store for Module<T: Config> as Nft {406407        // Private members408        NextCollectionID: CollectionId;409        CreatedCollectionCount: u32;410        ChainVersion: u64;411        ItemListIndex: map hasher(identity) CollectionId => TokenId;412413        // Chain limits struct414        pub ChainLimit get(fn chain_limit) config(): ChainLimits;415416        // Bound counters417        CollectionCount: u32;418        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;419420        // Basic collections421        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;422        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;423        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;424425        /// Balance owner per collection map426        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;427428        /// second parameter: item id + owner account id + spender account id429        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;430431        /// Item collections432        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;433        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;434        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;435436        /// Index list437        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;438439        /// Tokens transfer baskets440        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;441        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;442        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;443        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;444445        // Contract Sponsorship and Ownership446        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;447        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;448        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;449        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;450        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 451        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 452    }453    add_extra_genesis {454        build(|config: &GenesisConfig<T>| {455            // Modification of storage456            for (_num, _c) in &config.collection {457                <Module<T>>::init_collection(_c);458            }459460            for (_num, _c, _i) in &config.nft_item_id {461                <Module<T>>::init_nft_token(*_c, _i);462            }463464            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {465                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);466            }467468            for (_num, _c, _i) in &config.refungible_item_id {469                <Module<T>>::init_refungible_token(*_c, _i);470            }471        })472    }473}474475decl_event!(476    pub enum Event<T>477    where478        AccountId = <T as system::Config>::AccountId,479    {480        /// New collection was created481        /// 482        /// # Arguments483        /// 484        /// * collection_id: Globally unique identifier of newly created collection.485        /// 486        /// * mode: [CollectionMode] converted into u8.487        /// 488        /// * account_id: Collection owner.489        Created(CollectionId, u8, AccountId),490491        /// New item was created.492        /// 493        /// # Arguments494        /// 495        /// * collection_id: Id of the collection where item was created.496        /// 497        /// * item_id: Id of an item. Unique within the collection.498        ItemCreated(CollectionId, TokenId),499500        /// Collection item was burned.501        /// 502        /// # Arguments503        /// 504        /// collection_id.505        /// 506        /// item_id: Identifier of burned NFT.507        ItemDestroyed(CollectionId, TokenId),508    }509);510511decl_module! {512    pub struct Module<T: Config> for enum Call 513    where 514        origin: T::Origin515    {516        fn deposit_event() = default;517        type Error = Error<T>;518519        fn on_initialize(now: T::BlockNumber) -> Weight {520521            if ChainVersion::get() < 2522            {523                let value = NextCollectionID::get();524                CreatedCollectionCount::put(value);525                ChainVersion::put(2);526            }527528            0529        }530531        /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.532        /// 533        /// # Permissions534        /// 535        /// * Anyone.536        /// 537        /// # Arguments538        /// 539        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.540        /// 541        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.542        /// 543        /// * token_prefix: UTF-8 string with token prefix.544        /// 545        /// * mode: [CollectionMode] collection type and type dependent data.546        // returns collection ID547        #[weight = <T as Config>::WeightInfo::create_collection()]548        pub fn create_collection(origin,549                                 collection_name: Vec<u16>,550                                 collection_description: Vec<u16>,551                                 token_prefix: Vec<u8>,552                                 mode: CollectionMode) -> DispatchResult {553554            // Anyone can create a collection555            let who = ensure_signed(origin)?;556557            let decimal_points = match mode {558                CollectionMode::Fungible(points) => points,559                _ => 0560            };561562            // bound Total number of collections563            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);564565            // check params566            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);567            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);568            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);569            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);570571            // Generate next collection ID572            let next_id = CreatedCollectionCount::get()573                .checked_add(1)574                .ok_or(Error::<T>::NumOverflow)?;575576            // bound counter577            let total = CollectionCount::get()578                .checked_add(1)579                .ok_or(Error::<T>::NumOverflow)?;580581            CreatedCollectionCount::put(next_id);582            CollectionCount::put(total);583584            // Create new collection585            let new_collection = CollectionType {586                owner: who.clone(),587                name: collection_name,588                mode: mode.clone(),589                mint_mode: false,590                access: AccessMode::Normal,591                description: collection_description,592                decimal_points: decimal_points,593                token_prefix: token_prefix,594                offchain_schema: Vec::new(),595                schema_version: SchemaVersion::ImageURL,596                sponsor: T::AccountId::default(),597                sponsor_confirmed: false,598                variable_on_chain_schema: Vec::new(),599                const_on_chain_schema: Vec::new(),600                limits: CollectionLimits::default()601            };602603            // Add new collection to map604            <Collection<T>>::insert(next_id, new_collection);605606            // call event607            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));608609            Ok(())610        }611612        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.613        /// 614        /// # Permissions615        /// 616        /// * Collection Owner.617        /// 618        /// # Arguments619        /// 620        /// * collection_id: collection to destroy.621        #[weight = <T as Config>::WeightInfo::destroy_collection()]622        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {623624            let sender = ensure_signed(origin)?;625            Self::check_owner_permissions(collection_id, sender)?;626627            <AddressTokens<T>>::remove_prefix(collection_id);628            <Allowances<T>>::remove_prefix(collection_id);629            <Balance<T>>::remove_prefix(collection_id);630            <ItemListIndex>::remove(collection_id);631            <AdminList<T>>::remove(collection_id);632            <Collection<T>>::remove(collection_id);633            <WhiteList<T>>::remove_prefix(collection_id);634635            <NftItemList<T>>::remove_prefix(collection_id);636            <FungibleItemList<T>>::remove_prefix(collection_id);637            <ReFungibleItemList<T>>::remove_prefix(collection_id);638639            <NftTransferBasket<T>>::remove_prefix(collection_id);640            <FungibleTransferBasket<T>>::remove_prefix(collection_id);641            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);642643            if CollectionCount::get() > 0644            {645                // bound couter646                let total = CollectionCount::get()647                    .checked_sub(1)648                    .ok_or(Error::<T>::NumOverflow)?;649650                CollectionCount::put(total);651            }652653            Ok(())654        }655656        /// Add an address to white list.657        /// 658        /// # Permissions659        /// 660        /// * Collection Owner661        /// * Collection Admin662        /// 663        /// # Arguments664        /// 665        /// * collection_id.666        /// 667        /// * address.668        #[weight = <T as Config>::WeightInfo::add_to_white_list()]669        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{670671            let sender = ensure_signed(origin)?;672            Self::check_owner_or_admin_permissions(collection_id, sender)?;673674            <WhiteList<T>>::insert(collection_id, address, true);675            676            Ok(())677        }678679        /// Remove an address from white list.680        /// 681        /// # Permissions682        /// 683        /// * Collection Owner684        /// * Collection Admin685        /// 686        /// # Arguments687        /// 688        /// * collection_id.689        /// 690        /// * address.691        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]692        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{693694            let sender = ensure_signed(origin)?;695            Self::check_owner_or_admin_permissions(collection_id, sender)?;696697            <WhiteList<T>>::remove(collection_id, address);698699            Ok(())700        }701702        /// Toggle between normal and white list access for the methods with access for `Anyone`.703        /// 704        /// # Permissions705        /// 706        /// * Collection Owner.707        /// 708        /// # Arguments709        /// 710        /// * collection_id.711        /// 712        /// * mode: [AccessMode]713        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]714        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult715        {716            let sender = ensure_signed(origin)?;717718            Self::check_owner_permissions(collection_id, sender)?;719            let mut target_collection = <Collection<T>>::get(collection_id);720            target_collection.access = mode;721            <Collection<T>>::insert(collection_id, target_collection);722723            Ok(())724        }725726        /// Allows Anyone to create tokens if:727        /// * White List is enabled, and728        /// * Address is added to white list, and729        /// * This method was called with True parameter730        /// 731        /// # Permissions732        /// * Collection Owner733        ///734        /// # Arguments735        /// 736        /// * collection_id.737        /// 738        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.739        #[weight = <T as Config>::WeightInfo::set_mint_permission()]740        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult741        {742            let sender = ensure_signed(origin)?;743744            Self::check_owner_permissions(collection_id, sender)?;745            let mut target_collection = <Collection<T>>::get(collection_id);746            target_collection.mint_mode = mint_permission;747            <Collection<T>>::insert(collection_id, target_collection);748749            Ok(())750        }751752        /// Change the owner of the collection.753        /// 754        /// # Permissions755        /// 756        /// * Collection Owner.757        /// 758        /// # Arguments759        /// 760        /// * collection_id.761        /// 762        /// * new_owner.763        #[weight = <T as Config>::WeightInfo::change_collection_owner()]764        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {765766            let sender = ensure_signed(origin)?;767            Self::check_owner_permissions(collection_id, sender)?;768            let mut target_collection = <Collection<T>>::get(collection_id);769            target_collection.owner = new_owner;770            <Collection<T>>::insert(collection_id, target_collection);771772            Ok(())773        }774775        /// Adds an admin of the Collection.776        /// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership. 777        /// 778        /// # Permissions779        /// 780        /// * Collection Owner.781        /// * Collection Admin.782        /// 783        /// # Arguments784        /// 785        /// * collection_id: ID of the Collection to add admin for.786        /// 787        /// * new_admin_id: Address of new admin to add.788        #[weight = <T as Config>::WeightInfo::add_collection_admin()]789        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {790791            let sender = ensure_signed(origin)?;792            Self::check_owner_or_admin_permissions(collection_id, sender)?;793            let mut admin_arr: Vec<T::AccountId> = Vec::new();794795            if <AdminList<T>>::contains_key(collection_id)796            {797                admin_arr = <AdminList<T>>::get(collection_id);798                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);799            }800801            // Number of collection admins802            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);803804            admin_arr.push(new_admin_id);805            <AdminList<T>>::insert(collection_id, admin_arr);806807            Ok(())808        }809810        /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.811        ///812        /// # Permissions813        /// 814        /// * Collection Owner.815        /// * Collection Admin.816        /// 817        /// # Arguments818        /// 819        /// * collection_id: ID of the Collection to remove admin for.820        /// 821        /// * account_id: Address of admin to remove.822        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]823        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {824825            let sender = ensure_signed(origin)?;826            Self::check_owner_or_admin_permissions(collection_id, sender)?;827            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);828829            let mut admin_arr = <AdminList<T>>::get(collection_id);830            admin_arr.retain(|i| *i != account_id);831            <AdminList<T>>::insert(collection_id, admin_arr);832833            Ok(())834        }835836        /// # Permissions837        /// 838        /// * Collection Owner839        /// 840        /// # Arguments841        /// 842        /// * collection_id.843        /// 844        /// * new_sponsor.845        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]846        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {847848            let sender = ensure_signed(origin)?;849            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);850851            let mut target_collection = <Collection<T>>::get(collection_id);852            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);853854            target_collection.sponsor = new_sponsor;855            target_collection.sponsor_confirmed = false;856            <Collection<T>>::insert(collection_id, target_collection);857858            Ok(())859        }860861        /// # Permissions862        /// 863        /// * Sponsor.864        /// 865        /// # Arguments866        /// 867        /// * collection_id.868        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]869        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {870871            let sender = ensure_signed(origin)?;872            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);873874            let mut target_collection = <Collection<T>>::get(collection_id);875            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);876877            target_collection.sponsor_confirmed = true;878            <Collection<T>>::insert(collection_id, target_collection);879880            Ok(())881        }882883        /// Switch back to pay-per-own-transaction model.884        ///885        /// # Permissions886        ///887        /// * Collection owner.888        /// 889        /// # Arguments890        /// 891        /// * collection_id.892        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]893        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {894895            let sender = ensure_signed(origin)?;896            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);897898            let mut target_collection = <Collection<T>>::get(collection_id);899            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);900901            target_collection.sponsor = T::AccountId::default();902            target_collection.sponsor_confirmed = false;903            <Collection<T>>::insert(collection_id, target_collection);904905            Ok(())906        }907908        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.909        /// 910        /// # Permissions911        /// 912        /// * Collection Owner.913        /// * Collection Admin.914        /// * Anyone if915        ///     * White List is enabled, and916        ///     * Address is added to white list, and917        ///     * MintPermission is enabled (see SetMintPermission method)918        /// 919        /// # Arguments920        /// 921        /// * collection_id: ID of the collection.922        /// 923        /// * owner: Address, initial owner of the NFT.924        ///925        /// * data: Token data to store on chain.926        // #[weight =927        // (130_000_000 as Weight)928        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))929        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))930        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]931932        #[weight = <T as Config>::WeightInfo::create_item(data.len())]933        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {934935            let sender = ensure_signed(origin)?;936937            Self::collection_exists(collection_id)?;938939            let target_collection = <Collection<T>>::get(collection_id);940941            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;942            Self::validate_create_item_args(&target_collection, &data)?;943            Self::create_item_no_validation(collection_id, owner, data)?;944945            Ok(())946        }947948        /// This method creates multiple instances of NFT Collection created with CreateCollection method.949        /// 950        /// # Permissions951        /// 952        /// * Collection Owner.953        /// * Collection Admin.954        /// * Anyone if955        ///     * White List is enabled, and956        ///     * Address is added to white list, and957        ///     * MintPermission is enabled (see SetMintPermission method)958        /// 959        /// # Arguments960        /// 961        /// * collection_id: ID of the collection.962        /// 963        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].964        /// 965        /// * owner: Address, initial owner of the NFT.966        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()967                               .map(|data| { data.len() })968                               .sum())]969        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {970971            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);972            let sender = ensure_signed(origin)?;973974            Self::collection_exists(collection_id)?;975            let target_collection = <Collection<T>>::get(collection_id);976977            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;978979            for data in &items_data {980                Self::validate_create_item_args(&target_collection, data)?;981            }982            for data in &items_data {983                Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?;984            }985986            Ok(())987        }988989        /// Destroys a concrete instance of NFT.990        /// 991        /// # Permissions992        /// 993        /// * Collection Owner.994        /// * Collection Admin.995        /// * Current NFT Owner.996        /// 997        /// # Arguments998        /// 999        /// * collection_id: ID of the collection.1000        /// 1001        /// * item_id: ID of NFT to burn.1002        #[weight = <T as Config>::WeightInfo::burn_item()]1003        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10041005            let sender = ensure_signed(origin)?;1006            Self::collection_exists(collection_id)?;10071008            // Transfer permissions check1009            let target_collection = <Collection<T>>::get(collection_id);1010            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1011                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1012                Error::<T>::NoPermission);10131014            if target_collection.access == AccessMode::WhiteList {1015                Self::check_white_list(collection_id, &sender)?;1016            }10171018            match target_collection.mode1019            {1020                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1021                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1022                CollectionMode::ReFungible  => Self::burn_refungible_item(collection_id, item_id, &sender)?,1023                _ => ()1024            };10251026            // call event1027            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10281029            Ok(())1030        }10311032        /// Change ownership of the token.1033        /// 1034        /// # Permissions1035        /// 1036        /// * Collection Owner1037        /// * Collection Admin1038        /// * Current NFT owner1039        ///1040        /// # Arguments1041        /// 1042        /// * recipient: Address of token recipient.1043        /// 1044        /// * collection_id.1045        /// 1046        /// * item_id: ID of the item1047        ///     * Non-Fungible Mode: Required.1048        ///     * Fungible Mode: Ignored.1049        ///     * Re-Fungible Mode: Required.1050        /// 1051        /// * value: Amount to transfer.1052        ///     * Non-Fungible Mode: Ignored1053        ///     * Fungible Mode: Must specify transferred amount1054        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1055        #[weight = <T as Config>::WeightInfo::transfer()]1056        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1057            let sender = ensure_signed(origin)?;1058            Self::transfer_internal(sender, recipient, collection_id, item_id, value)1059        }10601061        /// Set, change, or remove approved address to transfer the ownership of the NFT.1062        /// 1063        /// # Permissions1064        /// 1065        /// * Collection Owner1066        /// * Collection Admin1067        /// * Current NFT owner1068        /// 1069        /// # Arguments1070        /// 1071        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1072        /// 1073        /// * collection_id.1074        /// 1075        /// * item_id: ID of the item.1076        #[weight = <T as Config>::WeightInfo::approve()]1077        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {10781079            let sender = ensure_signed(origin)?;10801081            Self::collection_exists(collection_id)?;1082            Self::token_exists(collection_id, item_id, &sender)?;10831084            // Transfer permissions check1085            let target_collection = <Collection<T>>::get(collection_id);1086            let allowance_limit = if Self::is_owner_or_admin_permissions(1087                collection_id,1088                sender.clone(),1089            ) {1090                None1091            } else if let Some(amount) = Self::owned_amount(1092                sender.clone(),1093                collection_id,1094                item_id,1095            ) {1096                Some(amount)1097            } else {1098                fail!(Error::<T>::NoPermission);1099            };11001101            if target_collection.access == AccessMode::WhiteList {1102                Self::check_white_list(collection_id, &sender)?;1103                Self::check_white_list(collection_id, &spender)?;1104            }11051106            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1107            let mut allowance: u128 = amount;1108            if allowance_exists {1109                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1110            }1111            if let Some(limit) = allowance_limit {1112                ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1113            }1114            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11151116            Ok(())1117        }1118        1119        /// 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.1120        /// 1121        /// # Permissions1122        /// * Collection Owner1123        /// * Collection Admin1124        /// * Current NFT owner1125        /// * Address approved by current NFT owner1126        /// 1127        /// # Arguments1128        /// 1129        /// * from: Address that owns token.1130        /// 1131        /// * recipient: Address of token recipient.1132        /// 1133        /// * collection_id.1134        /// 1135        /// * item_id: ID of the item.1136        /// 1137        /// * value: Amount to transfer.1138        #[weight = <T as Config>::WeightInfo::transfer_from()]1139        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11401141            let sender = ensure_signed(origin)?;1142            let mut appoved_transfer = false;11431144            // Check approval1145            let mut approval: u128 = 0;1146            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1147                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1148                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1149                appoved_transfer = true;1150            }11511152            let target_collection = <Collection<T>>::get(collection_id);11531154            // Limits check1155            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11561157            // Transfer permissions check         1158            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1159                Error::<T>::NoPermission);11601161            if target_collection.access == AccessMode::WhiteList {1162                Self::check_white_list(collection_id, &sender)?;1163                Self::check_white_list(collection_id, &recipient)?;1164            }11651166            // Reduce approval by transferred amount or remove if remaining approval drops to 01167            if approval.checked_sub(value).unwrap_or(0) > 0 {1168                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1169            }1170            else {1171                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1172            }11731174            match target_collection.mode1175            {1176                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1177                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1178                CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1179                _ => ()1180            };11811182            Ok(())1183        }11841185        // #[weight = 0]1186        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11871188        //     // let no_perm_mes = "You do not have permissions to modify this collection";1189        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1190        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1191        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11921193        //     // // on_nft_received  call11941195        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;11961197        //     Ok(())1198        // }11991200        /// Set off-chain data schema.1201        /// 1202        /// # Permissions1203        /// 1204        /// * Collection Owner1205        /// * Collection Admin1206        /// 1207        /// # Arguments1208        /// 1209        /// * collection_id.1210        /// 1211        /// * schema: String representing the offchain data schema.1212        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1213        pub fn set_variable_meta_data (1214            origin,1215            collection_id: CollectionId,1216            item_id: TokenId,1217            data: Vec<u8>1218        ) -> DispatchResult {1219            let sender = ensure_signed(origin)?;1220            1221            Self::collection_exists(collection_id)?;1222            Self::token_exists(collection_id, item_id, &sender)?;12231224            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12251226            // Modify permissions check1227            let target_collection = <Collection<T>>::get(collection_id);1228            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1229                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1230                Error::<T>::NoPermission);12311232            match target_collection.mode1233            {1234                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1235                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1236                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1237                _ => fail!(Error::<T>::UnexpectedCollectionType)1238            };12391240            Ok(())1241        }1242 1243        /// Set schema standard1244        /// ImageURL1245        /// Unique1246        /// 1247        /// # Permissions1248        /// 1249        /// * Collection Owner1250        /// * Collection Admin1251        /// 1252        /// # Arguments1253        /// 1254        /// * collection_id.1255        /// 1256        /// * schema: SchemaVersion: enum1257        #[weight = <T as Config>::WeightInfo::set_schema_version()]1258        pub fn set_schema_version(1259            origin,1260            collection_id: CollectionId,1261            version: SchemaVersion1262        ) -> DispatchResult {1263            let sender = ensure_signed(origin)?;1264            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1265            let mut target_collection = <Collection<T>>::get(collection_id);1266            target_collection.schema_version = version;1267            <Collection<T>>::insert(collection_id, target_collection);12681269            Ok(())1270        }12711272        /// Set off-chain data schema.1273        /// 1274        /// # Permissions1275        /// 1276        /// * Collection Owner1277        /// * Collection Admin1278        /// 1279        /// # Arguments1280        /// 1281        /// * collection_id.1282        /// 1283        /// * schema: String representing the offchain data schema.1284        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1285        pub fn set_offchain_schema(1286            origin,1287            collection_id: CollectionId,1288            schema: Vec<u8>1289        ) -> DispatchResult {1290            let sender = ensure_signed(origin)?;1291            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12921293            // check schema limit1294            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");12951296            let mut target_collection = <Collection<T>>::get(collection_id);1297            target_collection.offchain_schema = schema;1298            <Collection<T>>::insert(collection_id, target_collection);12991300            Ok(())1301        }13021303        /// Set const on-chain data schema.1304        /// 1305        /// # Permissions1306        /// 1307        /// * Collection Owner1308        /// * Collection Admin1309        /// 1310        /// # Arguments1311        /// 1312        /// * collection_id.1313        /// 1314        /// * schema: String representing the const on-chain data schema.1315        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1316        pub fn set_const_on_chain_schema (1317            origin,1318            collection_id: CollectionId,1319            schema: Vec<u8>1320        ) -> DispatchResult {1321            let sender = ensure_signed(origin)?;1322            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13231324            // check schema limit1325            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");13261327            let mut target_collection = <Collection<T>>::get(collection_id);1328            target_collection.const_on_chain_schema = schema;1329            <Collection<T>>::insert(collection_id, target_collection);13301331            Ok(())1332        }13331334        /// Set variable on-chain data schema.1335        /// 1336        /// # Permissions1337        /// 1338        /// * Collection Owner1339        /// * Collection Admin1340        /// 1341        /// # Arguments1342        /// 1343        /// * collection_id.1344        /// 1345        /// * schema: String representing the variable on-chain data schema.1346        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1347        pub fn set_variable_on_chain_schema (1348            origin,1349            collection_id: CollectionId,1350            schema: Vec<u8>1351        ) -> DispatchResult {1352            let sender = ensure_signed(origin)?;1353            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13541355            // check schema limit1356            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");13571358            let mut target_collection = <Collection<T>>::get(collection_id);1359            target_collection.variable_on_chain_schema = schema;1360            <Collection<T>>::insert(collection_id, target_collection);13611362            Ok(())1363        }13641365        // Sudo permissions function1366        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1367        pub fn set_chain_limits(1368            origin,1369            limits: ChainLimits1370        ) -> DispatchResult {13711372            #[cfg(not(feature = "runtime-benchmarks"))]1373            ensure_root(origin)?;13741375            <ChainLimit>::put(limits);1376            Ok(())1377        }13781379        /// Enable smart contract self-sponsoring.1380        /// 1381        /// # Permissions1382        /// 1383        /// * Contract Owner1384        /// 1385        /// # Arguments1386        /// 1387        /// * contract address1388        /// * enable flag1389        /// 1390        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1391        pub fn enable_contract_sponsoring(1392            origin,1393            contract_address: T::AccountId,1394            enable: bool1395        ) -> DispatchResult {13961397            let sender = ensure_signed(origin)?;13981399            #[cfg(feature = "runtime-benchmarks")]1400            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14011402            Self::ensure_contract_owned(sender, &contract_address)?;14031404            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1405            Ok(())1406        }14071408        /// Set the rate limit for contract sponsoring to specified number of blocks.1409        /// 1410        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1411        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1412        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1413        /// from contract endowment if there are at least B blocks between such transactions. 1414        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1415        /// 1416        /// # Permissions1417        /// 1418        /// * Contract Owner1419        /// 1420        /// # Arguments1421        /// 1422        /// -`contract_address`: Address of the contract to sponsor1423        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1424        /// 1425        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1426        pub fn set_contract_sponsoring_rate_limit(1427            origin,1428            contract_address: T::AccountId,1429            rate_limit: T::BlockNumber1430        ) -> DispatchResult {1431            let sender = ensure_signed(origin)?;14321433            #[cfg(feature = "runtime-benchmarks")]1434            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14351436            Self::ensure_contract_owned(sender, &contract_address)?;1437            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1438            Ok(())1439        }14401441        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1442        /// 1443        /// # Permissions1444        /// 1445        /// * Address that deployed smart contract.1446        /// 1447        /// # Arguments1448        /// 1449        /// -`contract_address`: Address of the contract.1450        /// 1451        /// - `enable`: .  1452        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1453        pub fn toggle_contract_white_list(1454            origin,1455            contract_address: T::AccountId,1456            enable: bool1457        ) -> DispatchResult {1458            let sender = ensure_signed(origin)?;14591460            #[cfg(feature = "runtime-benchmarks")]1461            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14621463            Self::ensure_contract_owned(sender, &contract_address)?;1464            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1465            Ok(())1466        }1467        1468        /// Add an address to smart contract white list.1469        /// 1470        /// # Permissions1471        /// 1472        /// * Address that deployed smart contract.1473        /// 1474        /// # Arguments1475        /// 1476        /// -`contract_address`: Address of the contract.1477        ///1478        /// -`account_address`: Address to add.1479        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1480        pub fn add_to_contract_white_list(1481            origin,1482            contract_address: T::AccountId,1483            account_address: T::AccountId1484        ) -> DispatchResult {1485            let sender = ensure_signed(origin)?;14861487            #[cfg(feature = "runtime-benchmarks")]1488            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1489            1490            Self::ensure_contract_owned(sender, &contract_address)?;      1491            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1492            Ok(())1493        }14941495        /// Remove an address from smart contract white list.1496        /// 1497        /// # Permissions1498        /// 1499        /// * Address that deployed smart contract.1500        /// 1501        /// # Arguments1502        /// 1503        /// -`contract_address`: Address of the contract.1504        ///1505        /// -`account_address`: Address to remove.1506        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1507        pub fn remove_from_contract_white_list(1508            origin,1509            contract_address: T::AccountId,1510            account_address: T::AccountId1511        ) -> DispatchResult {1512            let sender = ensure_signed(origin)?;15131514            #[cfg(feature = "runtime-benchmarks")]1515            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15161517            Self::ensure_contract_owned(sender, &contract_address)?;1518            <ContractWhiteList<T>>::remove(contract_address, account_address);1519            Ok(())1520        }15211522        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1523        pub fn set_collection_limits(1524            origin,1525            collection_id: u32,1526            limits: CollectionLimits,1527        ) -> DispatchResult {1528            let sender = ensure_signed(origin)?;1529            Self::check_owner_permissions(collection_id, sender.clone())?;1530            let mut target_collection = <Collection<T>>::get(collection_id);1531            let chain_limits = ChainLimit::get();1532            let climits = target_collection.limits;15331534            // collection bounds1535            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1536                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1537                Error::<T>::CollectionLimitBoundsExceeded);15381539            // token_limit   check  prev1540            ensure!(climits.token_limit > limits.token_limit && 1541                limits.token_limit <= chain_limits.account_token_ownership_limit, 1542                Error::<T>::AccountTokenLimitExceeded);15431544            target_collection.limits = limits;1545            <Collection<T>>::insert(collection_id, target_collection);15461547            Ok(())1548        } 1549    }1550}15511552impl<T: Config> Module<T> {15531554    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {15551556        let target_collection = <Collection<T>>::get(collection_id);15571558        // Limits check1559        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;15601561        // Transfer permissions check1562        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1563            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1564            Error::<T>::NoPermission);15651566        if target_collection.access == AccessMode::WhiteList {1567            Self::check_white_list(collection_id, &sender)?;1568            Self::check_white_list(collection_id, &recipient)?;1569        }15701571        match target_collection.mode1572        {1573            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1574            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1575            CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1576            _ => ()1577        };15781579        Ok(())1580    }158115821583    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15841585        // check token limit and account token limit1586        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1587        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1588        1589        Ok(())1590    }15911592    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15931594        // check token limit and account token limit1595        let total_items: u32 = ItemListIndex::get(collection_id);1596        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1597        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1598        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15991600        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1601            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1602            Self::check_white_list(collection_id, owner)?;1603            Self::check_white_list(collection_id, sender)?;1604        }16051606        Ok(())1607    }16081609    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1610        match target_collection.mode1611        {1612            CollectionMode::NFT => {1613                if let CreateItemData::NFT(data) = data {1614                    // check sizes1615                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1616                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1617                } else {1618                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1619                }1620            },1621            CollectionMode::Fungible(_) => {1622                if let CreateItemData::Fungible(_) = data {1623                } else {1624                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1625                }1626            },1627            CollectionMode::ReFungible => {1628                if let CreateItemData::ReFungible(data) = data {16291630                    // check sizes1631                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1632                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16331634                    // Check refungibility limits1635                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1636                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1637                } else {1638                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1639                }1640            },1641            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1642        };16431644        Ok(())1645    }16461647    fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1648        match data1649        {1650            CreateItemData::NFT(data) => {1651                let item = NftItemType {1652                    owner,1653                    const_data: data.const_data,1654                    variable_data: data.variable_data1655                };16561657                Self::add_nft_item(collection_id, item)?;1658            },1659            CreateItemData::Fungible(data) => {1660                Self::add_fungible_item(collection_id, &owner, data.value)?;1661            },1662            CreateItemData::ReFungible(data) => {1663                let mut owner_list = Vec::new();1664                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});16651666                let item = ReFungibleItemType {1667                    owner: owner_list,1668                    const_data: data.const_data,1669                    variable_data: data.variable_data1670                };16711672                Self::add_refungible_item(collection_id, item)?;1673            }1674        };16751676        // call event1677        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16781679        Ok(())1680    }16811682    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {16831684        // Does new owner already have an account?1685        let mut balance: u128 = 0;1686        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1687            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1688        } 16891690        // Mint 1691        let item = FungibleItemType {1692            value: balance + value1693        };1694        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16951696        // Update balance1697        let new_balance = <Balance<T>>::get(collection_id, owner)1698            .checked_add(value)1699            .ok_or(Error::<T>::NumOverflow)?;1700        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17011702        Ok(())1703    }17041705    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1706        let current_index = <ItemListIndex>::get(collection_id)1707            .checked_add(1)1708            .ok_or(Error::<T>::NumOverflow)?;1709        let itemcopy = item.clone();17101711        let value = item.owner.first().unwrap().fraction;1712        let owner = item.owner.first().unwrap().owner.clone();17131714        Self::add_token_index(collection_id, current_index, &owner)?;17151716        <ItemListIndex>::insert(collection_id, current_index);1717        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17181719        // Update balance1720        let new_balance = <Balance<T>>::get(collection_id, &owner)1721            .checked_add(value)1722            .ok_or(Error::<T>::NumOverflow)?;1723        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17241725        Ok(())1726    }17271728    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1729        let current_index = <ItemListIndex>::get(collection_id)1730            .checked_add(1)1731            .ok_or(Error::<T>::NumOverflow)?;17321733        let item_owner = item.owner.clone();1734        Self::add_token_index(collection_id, current_index, &item.owner)?;17351736        <ItemListIndex>::insert(collection_id, current_index);1737        <NftItemList<T>>::insert(collection_id, current_index, item);17381739        // Update balance1740        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1741            .checked_add(1)1742            .ok_or(Error::<T>::NumOverflow)?;1743        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17441745        Ok(())1746    }17471748    fn burn_refungible_item(1749        collection_id: CollectionId,1750        item_id: TokenId,1751        owner: &T::AccountId,1752    ) -> DispatchResult {1753        ensure!(1754            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1755            Error::<T>::TokenNotFound1756        );1757        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1758        let rft_balance = token1759            .owner1760            .iter()1761            .filter(|&i| i.owner == *owner)1762            .next()1763            .unwrap();1764        Self::remove_token_index(collection_id, item_id, owner)?;17651766        // update balance1767        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1768            .checked_sub(rft_balance.fraction)1769            .ok_or(Error::<T>::NumOverflow)?;1770        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);17711772        // Re-create owners list with sender removed1773        let index = token1774            .owner1775            .iter()1776            .position(|i| i.owner == *owner)1777            .unwrap();1778        token.owner.remove(index);1779        let owner_count = token.owner.len();17801781        // Burn the token completely if this was the last (only) owner1782        if owner_count == 0 {1783            <ReFungibleItemList<T>>::remove(collection_id, item_id);1784        }1785        else {1786            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1787        }17881789        Ok(())1790    }17911792    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1793        ensure!(1794            <NftItemList<T>>::contains_key(collection_id, item_id),1795            Error::<T>::TokenNotFound1796        );1797        let item = <NftItemList<T>>::get(collection_id, item_id);1798        Self::remove_token_index(collection_id, item_id, &item.owner)?;17991800        // update balance1801        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1802            .checked_sub(1)1803            .ok_or(Error::<T>::NumOverflow)?;1804        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1805        <NftItemList<T>>::remove(collection_id, item_id);18061807        Ok(())1808    }18091810    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1811        ensure!(1812            <FungibleItemList<T>>::contains_key(collection_id, owner),1813            Error::<T>::TokenNotFound1814        );1815        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1816        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18171818        // update balance1819        let new_balance = <Balance<T>>::get(collection_id, owner)1820            .checked_sub(value)1821            .ok_or(Error::<T>::NumOverflow)?;1822        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18231824        if balance.value - value > 0 {1825            balance.value -= value;1826            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1827        }1828        else {1829            <FungibleItemList<T>>::remove(collection_id, owner);1830        }18311832        Ok(())1833    }18341835    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1836        ensure!(1837            <Collection<T>>::contains_key(collection_id),1838            Error::<T>::CollectionNotFound1839        );1840        Ok(())1841    }18421843    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1844        Self::collection_exists(collection_id)?;18451846        let target_collection = <Collection<T>>::get(collection_id);1847        ensure!(1848            subject == target_collection.owner,1849            Error::<T>::NoPermission1850        );18511852        Ok(())1853    }18541855    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1856        let target_collection = <Collection<T>>::get(collection_id);1857        let mut result: bool = subject == target_collection.owner;1858        let exists = <AdminList<T>>::contains_key(collection_id);18591860        if !result & exists {1861            if <AdminList<T>>::get(collection_id).contains(&subject) {1862                result = true1863            }1864        }18651866        result1867    }18681869    fn check_owner_or_admin_permissions(1870        collection_id: CollectionId,1871        subject: T::AccountId,1872    ) -> DispatchResult {1873        Self::collection_exists(collection_id)?;1874        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18751876        ensure!(1877            result,1878            Error::<T>::NoPermission1879        );1880        Ok(())1881    }18821883    fn owned_amount(1884        subject: T::AccountId,1885        collection_id: CollectionId,1886        item_id: TokenId,1887    ) -> Option<u128> {1888        let target_collection = <Collection<T>>::get(collection_id);18891890        match target_collection.mode {1891            CollectionMode::NFT => {1892                if <NftItemList<T>>::get(collection_id, item_id).owner == subject {1893                    return Some(1)1894                }1895                None1896            },1897            CollectionMode::Fungible(_) => {1898                if <FungibleItemList<T>>::contains_key(collection_id, &subject) {1899                    return Some(<FungibleItemList<T>>::get(collection_id, &subject)1900                        .value);1901                }1902                None1903            },1904            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)1905                .owner1906                .iter()1907                .find(|i| i.owner == subject)1908                .map(|i| i.fraction),1909            CollectionMode::Invalid => None,1910        }1911    }19121913    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1914        let target_collection = <Collection<T>>::get(collection_id);19151916        match target_collection.mode {1917            CollectionMode::NFT => {1918                <NftItemList<T>>::get(collection_id, item_id).owner == subject1919            }1920            CollectionMode::Fungible(_) => {1921                <FungibleItemList<T>>::contains_key(collection_id, &subject)1922            }1923            CollectionMode::ReFungible => {1924                <ReFungibleItemList<T>>::get(collection_id, item_id)1925                    .owner1926                    .iter()1927                    .any(|i| i.owner == subject)1928            }1929            CollectionMode::Invalid => false,1930        }1931    }19321933    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1934        let mes = Error::<T>::AddresNotInWhiteList;1935        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);19361937        Ok(())1938    }19391940    /// Check if token exists. In case of Fungible, check if there is an entry for 1941    /// the owner in fungible balances double map1942    fn token_exists(1943        collection_id: CollectionId,1944        item_id: TokenId,1945        owner: &T::AccountId1946    ) -> DispatchResult {1947        let target_collection = <Collection<T>>::get(collection_id);1948        let exists = match target_collection.mode1949        {1950            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1951            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),1952            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1953            _ => false1954        };19551956        ensure!(exists == true, Error::<T>::TokenNotFound);1957        Ok(())1958    }19591960    fn transfer_fungible(1961        collection_id: CollectionId,1962        value: u128,1963        owner: &T::AccountId,1964        recipient: &T::AccountId,1965    ) -> DispatchResult {1966        Self::token_exists(collection_id, 0, owner)?;19671968        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1969        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19701971        // Send balance to recipient (updates balanceOf of recipient)1972        Self::add_fungible_item(collection_id, recipient, value)?;19731974        // update balanceOf of sender1975        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);19761977        // Reduce or remove sender1978        if balance.value == value {1979            <FungibleItemList<T>>::remove(collection_id, owner);1980        }1981        else {1982            balance.value -= value;1983            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1984        }19851986        Ok(())1987    }19881989    fn transfer_refungible(1990        collection_id: CollectionId,1991        item_id: TokenId,1992        value: u128,1993        owner: T::AccountId,1994        new_owner: T::AccountId,1995    ) -> DispatchResult {1996        Self::token_exists(collection_id, item_id, &owner)?;19971998        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1999        let item = full_item2000            .owner2001            .iter()2002            .filter(|i| i.owner == owner)2003            .next()2004            .ok_or(Error::<T>::NumOverflow)?;2005        let amount = item.fraction;20062007        ensure!(amount >= value, Error::<T>::TokenValueTooLow);20082009        // update balance2010        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2011            .checked_sub(value)2012            .ok_or(Error::<T>::NumOverflow)?;2013        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20142015        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2016            .checked_add(value)2017            .ok_or(Error::<T>::NumOverflow)?;2018        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20192020        let old_owner = item.owner.clone();2021        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20222023        // transfer2024        if amount == value && !new_owner_has_account {2025            // change owner2026            // new owner do not have account2027            let mut new_full_item = full_item.clone();2028            new_full_item2029                .owner2030                .iter_mut()2031                .find(|i| i.owner == owner)2032                .unwrap()2033                .owner = new_owner.clone();2034            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20352036            // update index collection2037            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2038        } else {2039            let mut new_full_item = full_item.clone();2040            new_full_item2041                .owner2042                .iter_mut()2043                .find(|i| i.owner == owner)2044                .unwrap()2045                .fraction -= value;20462047            // separate amount2048            if new_owner_has_account {2049                // new owner has account2050                new_full_item2051                    .owner2052                    .iter_mut()2053                    .find(|i| i.owner == new_owner)2054                    .unwrap()2055                    .fraction += value;2056            } else {2057                // new owner do not have account2058                new_full_item.owner.push(Ownership {2059                    owner: new_owner.clone(),2060                    fraction: value,2061                });2062                Self::add_token_index(collection_id, item_id, &new_owner)?;2063            }20642065            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2066        }20672068        Ok(())2069    }20702071    fn transfer_nft(2072        collection_id: CollectionId,2073        item_id: TokenId,2074        sender: T::AccountId,2075        new_owner: T::AccountId,2076    ) -> DispatchResult {2077        Self::token_exists(collection_id, item_id, &sender)?;20782079        let mut item = <NftItemList<T>>::get(collection_id, item_id);20802081        ensure!(2082            sender == item.owner,2083            Error::<T>::MustBeTokenOwner2084        );20852086        // update balance2087        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2088            .checked_sub(1)2089            .ok_or(Error::<T>::NumOverflow)?;2090        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20912092        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2093            .checked_add(1)2094            .ok_or(Error::<T>::NumOverflow)?;2095        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20962097        // change owner2098        let old_owner = item.owner.clone();2099        item.owner = new_owner.clone();2100        <NftItemList<T>>::insert(collection_id, item_id, item);21012102        // update index collection2103        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21042105        Ok(())2106    }2107    2108    fn set_re_fungible_variable_data(2109        collection_id: CollectionId,2110        item_id: TokenId,2111        data: Vec<u8>2112    ) -> DispatchResult {2113        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);21142115        item.variable_data = data;21162117        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21182119        Ok(())2120    }21212122    fn set_nft_variable_data(2123        collection_id: CollectionId,2124        item_id: TokenId,2125        data: Vec<u8>2126    ) -> DispatchResult {2127        let mut item = <NftItemList<T>>::get(collection_id, item_id);2128        2129        item.variable_data = data;21302131        <NftItemList<T>>::insert(collection_id, item_id, item);2132        2133        Ok(())2134    }21352136    fn init_collection(item: &CollectionType<T::AccountId>) {2137        // check params2138        assert!(2139            item.decimal_points <= MAX_DECIMAL_POINTS,2140            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2141        );2142        assert!(2143            item.name.len() <= 64,2144            "Collection name can not be longer than 63 char"2145        );2146        assert!(2147            item.name.len() <= 256,2148            "Collection description can not be longer than 255 char"2149        );2150        assert!(2151            item.token_prefix.len() <= 16,2152            "Token prefix can not be longer than 15 char"2153        );21542155        // Generate next collection ID2156        let next_id = CreatedCollectionCount::get()2157            .checked_add(1)2158            .unwrap();21592160        CreatedCollectionCount::put(next_id);2161    }21622163    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2164        let current_index = <ItemListIndex>::get(collection_id)2165            .checked_add(1)2166            .unwrap();21672168        let item_owner = item.owner.clone();2169        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21702171        <ItemListIndex>::insert(collection_id, current_index);21722173        // Update balance2174        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2175            .checked_add(1)2176            .unwrap();2177        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2178    }21792180    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2181        let current_index = <ItemListIndex>::get(collection_id)2182            .checked_add(1)2183            .unwrap();21842185        Self::add_token_index(collection_id, current_index, owner).unwrap();21862187        <ItemListIndex>::insert(collection_id, current_index);21882189        // Update balance2190        let new_balance = <Balance<T>>::get(collection_id, owner)2191            .checked_add(item.value)2192            .unwrap();2193        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2194    }21952196    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2197        let current_index = <ItemListIndex>::get(collection_id)2198            .checked_add(1)2199            .unwrap();22002201        let value = item.owner.first().unwrap().fraction;2202        let owner = item.owner.first().unwrap().owner.clone();22032204        Self::add_token_index(collection_id, current_index, &owner).unwrap();22052206        <ItemListIndex>::insert(collection_id, current_index);22072208        // Update balance2209        let new_balance = <Balance<T>>::get(collection_id, &owner)2210            .checked_add(value)2211            .unwrap();2212        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2213    }22142215    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {22162217        // add to account limit2218        if <AccountItemCount<T>>::contains_key(owner) {22192220            // bound Owned tokens by a single address2221            let count = <AccountItemCount<T>>::get(owner);2222            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);22232224            <AccountItemCount<T>>::insert(owner.clone(), count2225                .checked_add(1)2226                .ok_or(Error::<T>::NumOverflow)?);2227        }2228        else {2229            <AccountItemCount<T>>::insert(owner.clone(), 1);2230        }22312232        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2233        if list_exists {2234            let mut list = <AddressTokens<T>>::get(collection_id, owner);2235            let item_contains = list.contains(&item_index.clone());22362237            if !item_contains {2238                list.push(item_index.clone());2239            }22402241            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2242        } else {2243            let mut itm = Vec::new();2244            itm.push(item_index.clone());2245            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2246        }22472248        Ok(())2249    }22502251    fn remove_token_index(2252        collection_id: CollectionId,2253        item_index: TokenId,2254        owner: &T::AccountId,2255    ) -> DispatchResult {22562257        // update counter2258        <AccountItemCount<T>>::insert(owner.clone(), 2259            <AccountItemCount<T>>::get(owner)2260            .checked_sub(1)2261            .ok_or(Error::<T>::NumOverflow)?);226222632264        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2265        if list_exists {2266            let mut list = <AddressTokens<T>>::get(collection_id, owner);2267            let item_contains = list.contains(&item_index.clone());22682269            if item_contains {2270                list.retain(|&item| item != item_index);2271                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2272            }2273        }22742275        Ok(())2276    }22772278    fn move_token_index(2279        collection_id: CollectionId,2280        item_index: TokenId,2281        old_owner: &T::AccountId,2282        new_owner: &T::AccountId,2283    ) -> DispatchResult {2284        Self::remove_token_index(collection_id, item_index, old_owner)?;2285        Self::add_token_index(collection_id, item_index, new_owner)?;22862287        Ok(())2288    }2289    2290    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2291        if <ContractOwner<T>>::contains_key(contract.clone()) {2292            let owner = <ContractOwner<T>>::get(contract);2293            ensure!(account == owner, Error::<T>::NoPermission);2294        } else {2295            fail!(Error::<T>::NoPermission);2296        }22972298        Ok(())2299    }2300}23012302////////////////////////////////////////////////////////////////////////////////////////////////////2303// Economic models2304// #region23052306/// Fee multiplier.2307pub type Multiplier = FixedU128;23082309type BalanceOf<T> = <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::Balance;23102311/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2312/// in the queue.2313#[derive(Encode, Decode, Clone, Eq, PartialEq)]2314pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);23152316impl<T: Config + Send + Sync> sp_std::fmt::Debug 2317    for ChargeTransactionPayment<T>2318{2319	#[cfg(feature = "std")]2320	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2321		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2322	}2323	#[cfg(not(feature = "std"))]2324	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2325		Ok(())2326	}2327}23282329impl<T: Config> ChargeTransactionPayment<T>2330where2331    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2332    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2333    T::AccountId: AsRef<[u8]>,2334    T::AccountId: UncheckedFrom<T::Hash>,2335{2336    fn traditional_fee(2337        len: usize,2338        info: &DispatchInfoOf<T::Call>,2339        tip: BalanceOf<T>,2340    ) -> BalanceOf<T>2341    where2342        T::Call: Dispatchable<Info = DispatchInfo>,2343    {2344        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2345    }23462347	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2348        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2349        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2350        let len_saturation = max_block_length as u64 / (len as u64).max(1);2351        let coefficient: BalanceOf<T> = weight_saturation2352            .min(len_saturation)2353            .saturated_into::<BalanceOf<T>>();2354        final_fee2355            .saturating_mul(coefficient)2356            .saturated_into::<TransactionPriority>()2357    }23582359    fn withdraw_fee(2360        &self,2361        who: &T::AccountId,2362        call: &T::Call,2363        info: &DispatchInfoOf<T::Call>,2364        len: usize,2365	) -> Result<2366		(2367			BalanceOf<T>,2368			<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2369		),2370		TransactionValidityError,2371	> {2372        let tip = self.0;23732374        // Set fee based on call type. Creating collection costs 1 Unique.2375        // All other transactions have traditional fees so far2376        // let fee = match call.is_sub_type() {2377        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2378        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2379        //                                                 // _ => <BalanceOf<T>>::from(100)2380        // };2381        let fee = Self::traditional_fee(len, info, tip);23822383        // Only mess with balances if fee is not zero.2384        if fee.is_zero() {2385            return <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2386			.map(|i| (fee, i));2387        }23882389        // Determine who is paying transaction fee based on ecnomic model2390        // Parse call to extract collection ID and access collection sponsor2391        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2392            Some(Call::create_item(collection_id, _owner, _properties)) => {23932394                // sponsor timeout2395                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;23962397                let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;2398                let mut sponsored = true;2399                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2400                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2401                    let limit_time = last_tx_block + limit.into();2402                    if block_number <= limit_time {2403                        sponsored = false;2404                    }2405                }2406                if sponsored {2407                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2408                }24092410                // check free create limit2411                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2412                   (<Collection<T>>::get(collection_id).sponsor_confirmed) &&2413                   (sponsored)2414                {2415                    <Collection<T>>::get(collection_id).sponsor2416                } else {2417                    T::AccountId::default()2418                }2419            }2420            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2421                2422                let mut sponsor_transfer = false;2423                if <Collection<T>>::get(collection_id).sponsor_confirmed {24242425                    let collection_limits = <Collection<T>>::get(collection_id).limits;2426                    let collection_mode = <Collection<T>>::get(collection_id).mode;2427    2428                    // sponsor timeout2429                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2430                    sponsor_transfer = match collection_mode {2431                        CollectionMode::NFT => {2432    2433                            // get correct limit2434                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2435                                collection_limits.sponsor_transfer_timeout2436                            } else {2437                                ChainLimit::get().nft_sponsor_transfer_timeout2438                            };2439    2440                            let mut sponsored = true;2441                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2442                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2443                                let limit_time = last_tx_block + limit.into();2444                                if block_number <= limit_time {2445                                    sponsored = false;2446                                }2447                            }2448                            if sponsored {2449                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2450                            }24512452                            sponsored2453                        }2454                        CollectionMode::Fungible(_) => {2455    2456                            // get correct limit2457                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2458                                collection_limits.sponsor_transfer_timeout2459                            } else {2460                                ChainLimit::get().fungible_sponsor_transfer_timeout2461                            };2462    2463                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2464                            let mut sponsored = true;2465                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2466                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2467                                let limit_time = last_tx_block + limit.into();2468                                if block_number <= limit_time {2469                                    sponsored = false;2470                                }2471                            }2472                            if sponsored {2473                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2474                            }24752476                            sponsored2477                        }2478                        CollectionMode::ReFungible => {2479    2480                            // get correct limit2481                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2482                                collection_limits.sponsor_transfer_timeout2483                            } else {2484                                ChainLimit::get().refungible_sponsor_transfer_timeout2485                            };2486    2487                            let mut sponsored = true;2488                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2489                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2490                                let limit_time = last_tx_block + limit.into();2491                                if block_number <= limit_time {2492                                    sponsored = false;2493                                }2494                            }2495                            if sponsored {2496                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2497                            }24982499                            sponsored2500                        }2501                        _ => {2502                            false2503                        },2504                    };2505                }25062507                if !sponsor_transfer {2508                    T::AccountId::default()2509                } else {2510                    <Collection<T>>::get(collection_id).sponsor2511                }2512            }25132514            _ => T::AccountId::default(),2515        };25162517        // Sponsor smart contracts2518        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {25192520            // On instantiation: set the contract owner2521            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {25222523                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2524                    &who,2525                    code_hash,2526                    salt,2527                );2528                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());25292530                T::AccountId::default()2531            },25322533            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2534            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {25352536                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());25372538                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2539                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2540                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2541                  2542                if !owned_contract && white_list_enabled {2543                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2544                        return Err(InvalidTransaction::Call.into());2545                    }2546                }25472548                let mut sponsor_transfer = false;2549                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2550                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2551                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2552                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2553                    let limit_time = last_tx_block + rate_limit;25542555                    if block_number >= limit_time {2556                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2557                        sponsor_transfer = true;2558                    }2559                } else {2560                    sponsor_transfer = false;2561                }2562               2563                2564                let mut sp = T::AccountId::default();2565                if sponsor_transfer {2566                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2567                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2568                            sp = called_contract;2569                        }2570                    }2571                }25722573                sp2574            },25752576            _ => sponsor,2577        };25782579        let mut who_pays_fee: T::AccountId = sponsor.clone();2580        if sponsor == T::AccountId::default() {2581            who_pays_fee = who.clone();2582        }25832584		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2585			.map(|i| (fee, i))2586    }2587}258825892590impl<T: Config + Send + Sync> SignedExtension2591    for ChargeTransactionPayment<T>2592where2593    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2594    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2595    T::AccountId: AsRef<[u8]>,2596    T::AccountId: UncheckedFrom<T::Hash>,2597{2598    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2599    type AccountId = T::AccountId;2600    type Call = T::Call;2601    type AdditionalSigned = ();2602    type Pre = (2603        // tip2604        BalanceOf<T>,2605        // who pays fee2606        Self::AccountId,2607		// imbalance resulting from withdrawing the fee2608		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2609    );2610    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2611        Ok(())2612    }26132614    fn validate(2615        &self,2616        who: &Self::AccountId,2617        call: &Self::Call,2618        info: &DispatchInfoOf<Self::Call>,2619        len: usize,2620    ) -> TransactionValidity {2621		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2622		Ok(ValidTransaction {2623			priority: Self::get_priority(len, info, fee),2624			..Default::default()2625		})2626    }26272628    fn pre_dispatch(2629        self,2630        who: &Self::AccountId,2631        call: &Self::Call,2632        info: &DispatchInfoOf<Self::Call>,2633        len: usize,2634    ) -> Result<Self::Pre, TransactionValidityError> {2635        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2636        Ok((self.0, who.clone(), imbalance))2637    }26382639    fn post_dispatch(2640        pre: Self::Pre,2641        info: &DispatchInfoOf<Self::Call>,2642        post_info: &PostDispatchInfoOf<Self::Call>,2643        len: usize,2644        _result: &DispatchResult,2645    ) -> Result<(), TransactionValidityError> {2646		let (tip, who, imbalance) = pre;2647		let actual_fee = transaction_payment::Module::<T>::compute_actual_fee(2648			len as u32,2649			info,2650			post_info,2651			tip,2652		);2653		<T as transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2654		Ok(())2655    }2656}26572658// #endregion