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

difftreelog

NFTPAR-183: Storage Refactoring. Decimal points u32 -> u8. Also extended decimal points limit 4 -> 30.

sotmorskiy2020-12-10parent: #e7d3521.patch.diff
in: master

2 files changed

modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
before · pallets/nft/src/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]23#[cfg(feature = "std")]4pub use std::*;56#[cfg(feature = "std")]7pub use serde::*;89use codec::{Decode, Encode};10pub use frame_support::{11    construct_runtime, decl_event, decl_module, decl_storage, decl_error,12    dispatch::DispatchResult,13    ensure, fail, parameter_types,14    traits::{15        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,16        Randomness, WithdrawReason,17    },18    weights::{19        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},20        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,21        WeightToFeePolynomial,22    },23    IsSubType, StorageValue,24};2526use frame_system::{self as system, ensure_signed, ensure_root};27use sp_runtime::sp_std::prelude::Vec;28use sp_runtime::{29    traits::{30        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SignedExtension, Zero,31    },32    transaction_validity::{33        InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,34    },35    FixedPointOperand, FixedU128,36};37use pallet_contracts::ContractAddressFor;38use sp_runtime::traits::StaticLookup;3940#[cfg(test)]41mod mock;4243#[cfg(test)]44mod tests;4546mod default_weights;4748// Structs49// #region5051pub type CollectionId = u32;52pub type TokenId = u32;5354#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]55#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]56pub enum CollectionMode {57    Invalid,58    NFT,59    // decimal points60    Fungible(u32),61    // decimal points62    ReFungible(u32),63}6465impl Into<u8> for CollectionMode {66    fn into(self) -> u8 {67        match self {68            CollectionMode::Invalid => 0,69            CollectionMode::NFT => 1,70            CollectionMode::Fungible(_) => 2,71            CollectionMode::ReFungible(_) => 3,72        }73    }74}7576#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]77#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]78pub enum AccessMode {79    Normal,80    WhiteList,81}82impl Default for AccessMode {83    fn default() -> Self {84        Self::Normal85    }86}8788impl Default for CollectionMode {89    fn default() -> Self {90        Self::Invalid91    }92}9394#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]95#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]96pub struct Ownership<AccountId> {97    pub owner: AccountId,98    pub fraction: u128,99}100101#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]102#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]103pub struct CollectionType<AccountId> {104    pub owner: AccountId,105    pub mode: CollectionMode,106    pub access: AccessMode,107    pub decimal_points: u32,108    pub name: Vec<u16>,        // 64 include null escape char109    pub description: Vec<u16>, // 256 include null escape char110    pub token_prefix: Vec<u8>, // 16 include null escape char111    pub mint_mode: bool,112    pub offchain_schema: Vec<u8>,113    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender114    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship115    pub variable_on_chain_schema: Vec<u8>, //116    pub const_on_chain_schema: Vec<u8>, //117}118119#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]120#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]121pub struct NftItemType<AccountId> {122    pub collection: CollectionId,123    pub owner: AccountId,124    pub const_data: Vec<u8>,125    pub variable_data: Vec<u8>,126}127128#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]129#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]130pub struct FungibleItemType<AccountId> {131    pub collection: CollectionId,132    pub owner: AccountId,133    pub value: u128,134}135136#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]137#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]138pub struct ReFungibleItemType<AccountId> {139    pub collection: CollectionId,140    pub owner: Vec<Ownership<AccountId>>,141    pub const_data: Vec<u8>,142    pub variable_data: Vec<u8>,143}144145#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]146#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]147pub struct ApprovePermissions<AccountId> {148    pub approved: AccountId,149    pub amount: u64,150}151152#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]153#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]154pub struct VestingItem<AccountId, Moment> {155    pub sender: AccountId,156    pub recipient: AccountId,157    pub collection_id: CollectionId,158    pub item_id: TokenId,159    pub amount: u64,160    pub vesting_date: Moment,161}162163#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]164#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]165pub struct BasketItem<AccountId, BlockNumber> {166    pub address: AccountId,167    pub start_block: BlockNumber,168}169170#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]171#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]172pub struct ChainLimits {173    pub collection_numbers_limit: u32,174    pub account_token_ownership_limit: u32,175    pub collections_admins_limit: u64,176    pub custom_data_limit: u32,177178    // Timeouts for item types in passed blocks179    pub nft_sponsor_transfer_timeout: u32,180    pub fungible_sponsor_transfer_timeout: u32,181    pub refungible_sponsor_transfer_timeout: u32,182}183184pub trait WeightInfo {185	fn create_collection() -> Weight;186	fn destroy_collection() -> Weight;187	fn add_to_white_list() -> Weight;188	fn remove_from_white_list() -> Weight;189    fn set_public_access_mode() -> Weight;190    fn set_mint_permission() -> Weight;191    fn change_collection_owner() -> Weight;192    fn add_collection_admin() -> Weight;193    fn remove_collection_admin() -> Weight;194    fn set_collection_sponsor() -> Weight;195    fn confirm_sponsorship() -> Weight;196    fn remove_collection_sponsor() -> Weight;197    fn create_item(s: usize) -> Weight;198    fn burn_item() -> Weight;199    fn transfer() -> Weight;200    fn approve() -> Weight;201    fn transfer_from() -> Weight;202    fn set_offchain_schema() -> Weight;203    fn set_const_on_chain_schema() -> Weight;204    fn set_variable_on_chain_schema() -> Weight;205    fn set_variable_meta_data() -> Weight;206    fn enable_contract_sponsoring() -> Weight;207}208209#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]210#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]211pub struct CreateNftData {212    pub const_data: Vec<u8>,213    pub variable_data: Vec<u8>,214}215216#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]217#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]218pub struct CreateFungibleData {219}220221#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]222#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]223pub struct CreateReFungibleData {224    pub const_data: Vec<u8>,225    pub variable_data: Vec<u8>,226}227228#[derive(Encode, Decode, Debug, Clone, PartialEq)]229#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]230pub enum CreateItemData {231    NFT(CreateNftData),232    Fungible(CreateFungibleData),233    ReFungible(CreateReFungibleData)234}235236impl CreateItemData {237    pub fn len(&self) -> usize {238        let len = match self {239            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),240            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),241            _ => 0242        };243        244        return len;245    }246}247248impl From<CreateNftData> for CreateItemData {249    fn from(item: CreateNftData) -> Self {250        CreateItemData::NFT(item)251    }252}253254impl From<CreateReFungibleData> for CreateItemData {255    fn from(item: CreateReFungibleData) -> Self {256        CreateItemData::ReFungible(item)257    }258}259260impl From<CreateFungibleData> for CreateItemData {261    fn from(item: CreateFungibleData) -> Self {262        CreateItemData::Fungible(item)263    }264}265266267decl_error! {268	/// Error for non-fungible-token module.269	pub enum Error for Module<T: Trait> {270        /// Total collections bound exceeded.271        TotalCollectionsLimitExceeded,272		/// Decimal_points parameter must be lower than 4.273        CollectionDecimalPointLimitExceeded, 274        /// Collection name can not be longer than 63 char.275        CollectionNameLimitExceeded, 276        /// Collection description can not be longer than 255 char.277        CollectionDescriptionLimitExceeded, 278        /// Token prefix can not be longer than 15 char.279        CollectionTokenPrefixLimitExceeded,280        /// This collection does not exist.281        CollectionNotFound,282        /// Item not exists.283        TokenNotFound,284        /// Arithmetic calculation overflow.285        NumOverflow,       286        /// Account already has admin role.287        AlreadyAdmin,  288        /// You do not own this collection.289        NoPermission,290        /// This address is not set as sponsor, use setCollectionSponsor first.291        ConfirmUnsetSponsorFail,292        /// Collection is not in mint mode.293        PublicMintingNotAllowed,294        /// Sender parameter and item owner must be equal.295        MustBeTokenOwner,296        /// Item balance not enough.297        TokenValueTooLow,298        /// Size of item is too large.299        NftSizeLimitExceeded,300        /// No approve found301        ApproveNotFound,302        /// Requested value more than approved.303        TokenValueNotEnough,304        /// Only approved addresses can call this method.305        ApproveRequired,306        /// Address is not in white list.307        AddresNotInWhiteList,308        /// Number of collection admins bound exceeded.309        CollectionAdminsLimitExceeded,310        /// Owned tokens by a single address bound exceeded.311        AddressOwnershipLimitExceeded,312        /// Length of items properties must be greater than 0.313        EmptyArgument,314        /// const_data exceeded data limit.315        TokenConstDataLimitExceeded,316        /// variable_data exceeded data limit.317        TokenVariableDataLimitExceeded,318        /// Not NFT item data used to mint in NFT collection.319        NotNftDataUsedToMintNftCollectionToken,320        /// Not Fungible item data used to mint in Fungible collection.321        NotFungibleDataUsedToMintFungibleCollectionToken,322        /// Not Re Fungible item data used to mint in Re Fungible collection.323        NotReFungibleDataUsedToMintReFungibleCollectionToken,324        /// Unexpected collection type.325        UnexpectedCollectionType,326        /// Can't store metadata in fungible tokens.327        CantStoreMetadataInFungibleTokens328	}329}330331pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {332    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;333334    /// Weight information for extrinsics in this pallet.335	type WeightInfo: WeightInfo;336}337338#[cfg(feature = "runtime-benchmarks")]339mod benchmarking;340341// #endregion342343decl_storage! {344    trait Store for Module<T: Trait> as Nft {345346        // Private members347        NextCollectionID: CollectionId;348        CreatedCollectionCount: u32;349        ChainVersion: u64;350        ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;351352        // Chain limits struct353        pub ChainLimit get(fn chain_limit) config(): ChainLimits;354355        // Bound counters356        CollectionCount: u32;357        pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u32;358359        // Basic collections360        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;361        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;362        pub WhiteList get(fn white_list): map hasher(identity) CollectionId => Vec<T::AccountId>;363364        /// Balance owner per collection map365        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => u64;366367        /// second parameter: item id + owner account id368        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;369370        /// Item collections371        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => NftItemType<T::AccountId>;372        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => FungibleItemType<T::AccountId>;373        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => ReFungibleItemType<T::AccountId>;374375        /// Index list376        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;377378        /// Tokens transfer baskets379        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;380        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Vec<BasketItem<T::AccountId, T::BlockNumber>>;381        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;382383        // Contract Sponsorship and Ownership384        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;385        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;386        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;387        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;388    }389    add_extra_genesis {390        build(|config: &GenesisConfig<T>| {391            // Modification of storage392            for (_num, _c) in &config.collection {393                <Module<T>>::init_collection(_c);394            }395396            for (_num, _q, _i) in &config.nft_item_id {397                <Module<T>>::init_nft_token(_i);398            }399400            for (_num, _q, _i) in &config.fungible_item_id {401                <Module<T>>::init_fungible_token(_i);402            }403404            for (_num, _q, _i) in &config.refungible_item_id {405                <Module<T>>::init_refungible_token(_i);406            }407        })408    }409}410411decl_event!(412    pub enum Event<T>413    where414        AccountId = <T as system::Trait>::AccountId,415    {416        /// New collection was created417        /// 418        /// # Arguments419        /// 420        /// * collection_id: Globally unique identifier of newly created collection.421        /// 422        /// * mode: [CollectionMode] converted into u8.423        /// 424        /// * account_id: Collection owner.425        Created(CollectionId, u8, AccountId),426427        /// New item was created.428        /// 429        /// # Arguments430        /// 431        /// * collection_id: Id of the collection where item was created.432        /// 433        /// * item_id: Id of an item. Unique within the collection.434        ItemCreated(CollectionId, TokenId),435436        /// Collection item was burned.437        /// 438        /// # Arguments439        /// 440        /// collection_id.441        /// 442        /// item_id: Identifier of burned NFT.443        ItemDestroyed(CollectionId, TokenId),444    }445);446447decl_module! {448    pub struct Module<T: Trait> for enum Call where origin: T::Origin {449450        fn deposit_event() = default;451        type Error = Error<T>;452453        fn on_initialize(now: T::BlockNumber) -> Weight {454455            if ChainVersion::get() < 2456            {457                let value = NextCollectionID::get();458                CreatedCollectionCount::put(value);459                ChainVersion::put(2);460            }461462            0463        }464465        /// 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.466        /// 467        /// # Permissions468        /// 469        /// * Anyone.470        /// 471        /// # Arguments472        /// 473        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.474        /// 475        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.476        /// 477        /// * token_prefix: UTF-8 string with token prefix.478        /// 479        /// * mode: [CollectionMode] collection type and type dependent data.480        // returns collection ID481        #[weight = T::WeightInfo::create_collection()]482        pub fn create_collection(origin,483                                 collection_name: Vec<u16>,484                                 collection_description: Vec<u16>,485                                 token_prefix: Vec<u8>,486                                 mode: CollectionMode) -> DispatchResult {487488            // Anyone can create a collection489            let who = ensure_signed(origin)?;490491            let decimal_points = match mode {492                CollectionMode::Fungible(points) => points,493                CollectionMode::ReFungible(points) => points,494                _ => 0495            };496497            // bound Total number of collections498            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);499500            // check params501            ensure!(decimal_points <= 4, Error::<T>::CollectionDecimalPointLimitExceeded);502503            let mut name = collection_name.to_vec();504            name.push(0);505            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);506507            let mut description = collection_description.to_vec();508            description.push(0);509            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);510511            let mut prefix = token_prefix.to_vec();512            prefix.push(0);513            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);514515            // Generate next collection ID516            let next_id = CreatedCollectionCount::get()517                .checked_add(1)518                .ok_or(Error::<T>::NumOverflow)?;519520            // bound counter521            let total = CollectionCount::get()522                .checked_add(1)523                .ok_or(Error::<T>::NumOverflow)?;524525            CreatedCollectionCount::put(next_id);526            CollectionCount::put(total);527528            // Create new collection529            let new_collection = CollectionType {530                owner: who.clone(),531                name: name,532                mode: mode.clone(),533                mint_mode: false,534                access: AccessMode::Normal,535                description: description,536                decimal_points: decimal_points,537                token_prefix: prefix,538                offchain_schema: Vec::new(),539                sponsor: T::AccountId::default(),540                unconfirmed_sponsor: T::AccountId::default(),541                variable_on_chain_schema: Vec::new(),542                const_on_chain_schema: Vec::new(),543            };544545            // Add new collection to map546            <Collection<T>>::insert(next_id, new_collection);547548            // call event549            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));550551            Ok(())552        }553554        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.555        /// 556        /// # Permissions557        /// 558        /// * Collection Owner.559        /// 560        /// # Arguments561        /// 562        /// * collection_id: collection to destroy.563        #[weight = T::WeightInfo::destroy_collection()]564        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {565566            let sender = ensure_signed(origin)?;567            Self::check_owner_permissions(collection_id, sender)?;568569            <AddressTokens<T>>::remove_prefix(collection_id);570            <ApprovedList<T>>::remove_prefix(collection_id);571            <Balance<T>>::remove_prefix(collection_id);572            <ItemListIndex>::remove(collection_id);573            <AdminList<T>>::remove(collection_id);574            <Collection<T>>::remove(collection_id);575            <WhiteList<T>>::remove(collection_id);576577            <NftItemList<T>>::remove_prefix(collection_id);578            <FungibleItemList<T>>::remove_prefix(collection_id);579            <ReFungibleItemList<T>>::remove_prefix(collection_id);580581            <NftTransferBasket<T>>::remove_prefix(collection_id);582            <FungibleTransferBasket<T>>::remove_prefix(collection_id);583            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);584585            if CollectionCount::get() > 0586            {587                // bound couter588                let total = CollectionCount::get()589                    .checked_sub(1)590                    .ok_or(Error::<T>::NumOverflow)?;591592                CollectionCount::put(total);593            }594595            Ok(())596        }597598        /// Add an address to white list.599        /// 600        /// # Permissions601        /// 602        /// * Collection Owner603        /// * Collection Admin604        /// 605        /// # Arguments606        /// 607        /// * collection_id.608        /// 609        /// * address.610        #[weight = T::WeightInfo::add_to_white_list()]611        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{612613            let sender = ensure_signed(origin)?;614            Self::check_owner_or_admin_permissions(collection_id, sender)?;615616            let mut white_list_collection: Vec<T::AccountId>;617            if <WhiteList<T>>::contains_key(collection_id) {618                white_list_collection = <WhiteList<T>>::get(collection_id);619                if !white_list_collection.contains(&address.clone())620                {621                    white_list_collection.push(address.clone());622                }623            }624            else {625                white_list_collection = Vec::new();626                white_list_collection.push(address.clone());627            }628629            <WhiteList<T>>::insert(collection_id, white_list_collection);630            Ok(())631        }632633        /// Remove an address from white list.634        /// 635        /// # Permissions636        /// 637        /// * Collection Owner638        /// * Collection Admin639        /// 640        /// # Arguments641        /// 642        /// * collection_id.643        /// 644        /// * address.645        #[weight = T::WeightInfo::remove_from_white_list()]646        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{647648            let sender = ensure_signed(origin)?;649            Self::check_owner_or_admin_permissions(collection_id, sender)?;650651            if <WhiteList<T>>::contains_key(collection_id) {652                let mut white_list_collection = <WhiteList<T>>::get(collection_id);653                if white_list_collection.contains(&address.clone())654                {655                    white_list_collection.retain(|i| *i != address.clone());656                    <WhiteList<T>>::insert(collection_id, white_list_collection);657                }658            }659660            Ok(())661        }662663        /// Toggle between normal and white list access for the methods with access for `Anyone`.664        /// 665        /// # Permissions666        /// 667        /// * Collection Owner.668        /// 669        /// # Arguments670        /// 671        /// * collection_id.672        /// 673        /// * mode: [AccessMode]674        #[weight = T::WeightInfo::set_public_access_mode()]675        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult676        {677            let sender = ensure_signed(origin)?;678679            Self::check_owner_permissions(collection_id, sender)?;680            let mut target_collection = <Collection<T>>::get(collection_id);681            target_collection.access = mode;682            <Collection<T>>::insert(collection_id, target_collection);683684            Ok(())685        }686687        /// Allows Anyone to create tokens if:688        /// * White List is enabled, and689        /// * Address is added to white list, and690        /// * This method was called with True parameter691        /// 692        /// # Permissions693        /// * Collection Owner694        ///695        /// # Arguments696        /// 697        /// * collection_id.698        /// 699        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.700        #[weight = T::WeightInfo::set_mint_permission()]701        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult702        {703            let sender = ensure_signed(origin)?;704705            Self::check_owner_permissions(collection_id, sender)?;706            let mut target_collection = <Collection<T>>::get(collection_id);707            target_collection.mint_mode = mint_permission;708            <Collection<T>>::insert(collection_id, target_collection);709710            Ok(())711        }712713        /// Change the owner of the collection.714        /// 715        /// # Permissions716        /// 717        /// * Collection Owner.718        /// 719        /// # Arguments720        /// 721        /// * collection_id.722        /// 723        /// * new_owner.724        #[weight = T::WeightInfo::change_collection_owner()]725        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {726727            let sender = ensure_signed(origin)?;728            Self::check_owner_permissions(collection_id, sender)?;729            let mut target_collection = <Collection<T>>::get(collection_id);730            target_collection.owner = new_owner;731            <Collection<T>>::insert(collection_id, target_collection);732733            Ok(())734        }735736        /// Adds an admin of the Collection.737        /// 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. 738        /// 739        /// # Permissions740        /// 741        /// * Collection Owner.742        /// * Collection Admin.743        /// 744        /// # Arguments745        /// 746        /// * collection_id: ID of the Collection to add admin for.747        /// 748        /// * new_admin_id: Address of new admin to add.749        #[weight = T::WeightInfo::add_collection_admin()]750        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {751752            let sender = ensure_signed(origin)?;753            Self::check_owner_or_admin_permissions(collection_id, sender)?;754            let mut admin_arr: Vec<T::AccountId> = Vec::new();755756            if <AdminList<T>>::contains_key(collection_id)757            {758                admin_arr = <AdminList<T>>::get(collection_id);759                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);760            }761762            // Number of collection admins763            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);764765            admin_arr.push(new_admin_id);766            <AdminList<T>>::insert(collection_id, admin_arr);767768            Ok(())769        }770771        /// 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.772        ///773        /// # Permissions774        /// 775        /// * Collection Owner.776        /// * Collection Admin.777        /// 778        /// # Arguments779        /// 780        /// * collection_id: ID of the Collection to remove admin for.781        /// 782        /// * account_id: Address of admin to remove.783        #[weight = T::WeightInfo::remove_collection_admin()]784        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {785786            let sender = ensure_signed(origin)?;787            Self::check_owner_or_admin_permissions(collection_id, sender)?;788789            if <AdminList<T>>::contains_key(collection_id)790            {791                let mut admin_arr = <AdminList<T>>::get(collection_id);792                admin_arr.retain(|i| *i != account_id);793                <AdminList<T>>::insert(collection_id, admin_arr);794            }795796            Ok(())797        }798799        /// # Permissions800        /// 801        /// * Collection Owner802        /// 803        /// # Arguments804        /// 805        /// * collection_id.806        /// 807        /// * new_sponsor.808        #[weight = T::WeightInfo::set_collection_sponsor()]809        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {810811            let sender = ensure_signed(origin)?;812            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);813814            let mut target_collection = <Collection<T>>::get(collection_id);815            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);816817            target_collection.unconfirmed_sponsor = new_sponsor;818            <Collection<T>>::insert(collection_id, target_collection);819820            Ok(())821        }822823        /// # Permissions824        /// 825        /// * Sponsor.826        /// 827        /// # Arguments828        /// 829        /// * collection_id.830        #[weight = T::WeightInfo::confirm_sponsorship()]831        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {832833            let sender = ensure_signed(origin)?;834            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);835836            let mut target_collection = <Collection<T>>::get(collection_id);837            ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);838839            target_collection.sponsor = target_collection.unconfirmed_sponsor;840            target_collection.unconfirmed_sponsor = T::AccountId::default();841            <Collection<T>>::insert(collection_id, target_collection);842843            Ok(())844        }845846        /// Switch back to pay-per-own-transaction model.847        ///848        /// # Permissions849        ///850        /// * Collection owner.851        /// 852        /// # Arguments853        /// 854        /// * collection_id.855        #[weight = T::WeightInfo::remove_collection_sponsor()]856        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {857858            let sender = ensure_signed(origin)?;859            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);860861            let mut target_collection = <Collection<T>>::get(collection_id);862            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);863864            target_collection.sponsor = T::AccountId::default();865            <Collection<T>>::insert(collection_id, target_collection);866867            Ok(())868        }869870        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.871        /// 872        /// # Permissions873        /// 874        /// * Collection Owner.875        /// * Collection Admin.876        /// * Anyone if877        ///     * White List is enabled, and878        ///     * Address is added to white list, and879        ///     * MintPermission is enabled (see SetMintPermission method)880        /// 881        /// # Arguments882        /// 883        /// * collection_id: ID of the collection.884        /// 885        /// * owner: Address, initial owner of the NFT.886        ///887        /// * data: Token data to store on chain.888        // #[weight =889        // (130_000_000 as Weight)890        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))891        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))892        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]893894        #[weight = T::WeightInfo::create_item(data.len())]895        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {896897            let sender = ensure_signed(origin)?;898899            Self::collection_exists(collection_id)?;900901            let target_collection = <Collection<T>>::get(collection_id);902903            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;904            Self::validate_create_item_args(&target_collection, &data)?;905            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;906907            Ok(())908        }909910        /// This method creates multiple instances of NFT Collection created with CreateCollection method.911        /// 912        /// # Permissions913        /// 914        /// * Collection Owner.915        /// * Collection Admin.916        /// * Anyone if917        ///     * White List is enabled, and918        ///     * Address is added to white list, and919        ///     * MintPermission is enabled (see SetMintPermission method)920        /// 921        /// # Arguments922        /// 923        /// * collection_id: ID of the collection.924        /// 925        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].926        /// 927        /// * owner: Address, initial owner of the NFT.928        #[weight = T::WeightInfo::create_item(items_data.into_iter()929                               .map(|data| { data.len() })930                               .sum())]931        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {932933            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);934            let sender = ensure_signed(origin)?;935936            Self::collection_exists(collection_id)?;937            let target_collection = <Collection<T>>::get(collection_id);938939            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;940941            for data in &items_data {942                Self::validate_create_item_args(&target_collection, data)?;943            }944            for data in &items_data {945                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;946            }947948            Ok(())949        }950951        /// Destroys a concrete instance of NFT.952        /// 953        /// # Permissions954        /// 955        /// * Collection Owner.956        /// * Collection Admin.957        /// * Current NFT Owner.958        /// 959        /// # Arguments960        /// 961        /// * collection_id: ID of the collection.962        /// 963        /// * item_id: ID of NFT to burn.964        #[weight = T::WeightInfo::burn_item()]965        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {966967            let sender = ensure_signed(origin)?;968            Self::collection_exists(collection_id)?;969970            // Transfer permissions check971            let target_collection = <Collection<T>>::get(collection_id);972            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||973                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),974                Error::<T>::NoPermission);975976            if target_collection.access == AccessMode::WhiteList {977                Self::check_white_list(collection_id, &sender)?;978            }979980            match target_collection.mode981            {982                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,983                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,984                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,985                _ => ()986            };987988            // call event989            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));990991            Ok(())992        }993994        /// Change ownership of the token.995        /// 996        /// # Permissions997        /// 998        /// * Collection Owner999        /// * Collection Admin1000        /// * Current NFT owner1001        ///1002        /// # Arguments1003        /// 1004        /// * recipient: Address of token recipient.1005        /// 1006        /// * collection_id.1007        /// 1008        /// * item_id: ID of the item1009        ///     * Non-Fungible Mode: Required.1010        ///     * Fungible Mode: Ignored.1011        ///     * Re-Fungible Mode: Required.1012        /// 1013        /// * value: Amount to transfer.1014        ///     * Non-Fungible Mode: Ignored1015        ///     * Fungible Mode: Must specify transferred amount1016        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1017        #[weight = T::WeightInfo::transfer()]1018        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u64) -> DispatchResult {10191020            let sender = ensure_signed(origin)?;10211022            // Transfer permissions check1023            let target_collection = <Collection<T>>::get(collection_id);1024            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1025                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1026                Error::<T>::NoPermission);10271028            if target_collection.access == AccessMode::WhiteList {1029                Self::check_white_list(collection_id, &sender)?;1030                Self::check_white_list(collection_id, &recipient)?;1031            }10321033            match target_collection.mode1034            {1035                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1036                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1037                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1038                _ => ()1039            };10401041            Ok(())1042        }10431044        /// Set, change, or remove approved address to transfer the ownership of the NFT.1045        /// 1046        /// # Permissions1047        /// 1048        /// * Collection Owner1049        /// * Collection Admin1050        /// * Current NFT owner1051        /// 1052        /// # Arguments1053        /// 1054        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1055        /// 1056        /// * collection_id.1057        /// 1058        /// * item_id: ID of the item.1059        #[weight = T::WeightInfo::approve()]1060        pub fn approve(origin, approved: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {10611062            let sender = ensure_signed(origin)?;10631064            // Transfer permissions check1065            let target_collection = <Collection<T>>::get(collection_id);1066            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1067                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1068                Error::<T>::NoPermission);10691070            if target_collection.access == AccessMode::WhiteList {1071                Self::check_white_list(collection_id, &sender)?;1072                Self::check_white_list(collection_id, &approved)?;1073            }10741075            // amount param stub1076            let amount = 100000000;10771078            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1079            if list_exists {10801081                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1082                let item_contains = list.iter().any(|i| i.approved == approved);10831084                if !item_contains {1085                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1086                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1087                }1088            } else {10891090                let mut list = Vec::new();1091                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1092                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1093            }10941095            Ok(())1096        }1097        1098        /// 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.1099        /// 1100        /// # Permissions1101        /// * Collection Owner1102        /// * Collection Admin1103        /// * Current NFT owner1104        /// * Address approved by current NFT owner1105        /// 1106        /// # Arguments1107        /// 1108        /// * from: Address that owns token.1109        /// 1110        /// * recipient: Address of token recipient.1111        /// 1112        /// * collection_id.1113        /// 1114        /// * item_id: ID of the item.1115        /// 1116        /// * value: Amount to transfer.1117        #[weight = T::WeightInfo::transfer_from()]1118        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u64 ) -> DispatchResult {11191120            let sender = ensure_signed(origin)?;1121            let mut appoved_transfer = false;11221123            // Check approve1124            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1125                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1126                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1127                if opt_item.is_some()1128                {1129                    appoved_transfer = true;1130                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1131                }1132            }11331134            // Transfer permissions check1135            let target_collection = <Collection<T>>::get(collection_id);1136                ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1137                Error::<T>::NoPermission);11381139            if target_collection.access == AccessMode::WhiteList {1140                Self::check_white_list(collection_id, &sender)?;1141                Self::check_white_list(collection_id, &recipient)?;1142            }11431144            // remove approve1145            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1146                .into_iter().filter(|i| i.approved != sender.clone()).collect();1147            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);114811491150            match target_collection.mode1151            {1152                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1153                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1154                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1155                _ => ()1156            };11571158            Ok(())1159        }11601161        ///1162        #[weight = 0]1163        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11641165            // let no_perm_mes = "You do not have permissions to modify this collection";1166            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1167            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1168            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11691170            // // on_nft_received  call11711172            // Self::transfer(origin, collection_id, item_id, new_owner)?;11731174            Ok(())1175        }11761177        /// Set off-chain data schema.1178        /// 1179        /// # Permissions1180        /// 1181        /// * Collection Owner1182        /// * Collection Admin1183        /// 1184        /// # Arguments1185        /// 1186        /// * collection_id.1187        /// 1188        /// * schema: String representing the offchain data schema.1189        #[weight = T::WeightInfo::set_variable_meta_data()]1190        pub fn set_variable_meta_data (1191            origin,1192            collection_id: CollectionId,1193            item_id: TokenId,1194            data: Vec<u8>1195        ) -> DispatchResult {1196            let sender = ensure_signed(origin)?;1197            1198            Self::collection_exists(collection_id)?;1199            1200            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12011202            // Modify permissions check1203            let target_collection = <Collection<T>>::get(collection_id);1204            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1205                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1206                Error::<T>::NoPermission);12071208            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12091210            match target_collection.mode1211            {1212                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1213                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1214                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1215                _ => fail!(Error::<T>::UnexpectedCollectionType)1216            };12171218            Ok(())1219        }1220        12211222        /// Set off-chain data schema.1223        /// 1224        /// # Permissions1225        /// 1226        /// * Collection Owner1227        /// * Collection Admin1228        /// 1229        /// # Arguments1230        /// 1231        /// * collection_id.1232        /// 1233        /// * schema: String representing the offchain data schema.1234        #[weight = T::WeightInfo::set_offchain_schema()]1235        pub fn set_offchain_schema(1236            origin,1237            collection_id: CollectionId,1238            schema: Vec<u8>1239        ) -> DispatchResult {1240            let sender = ensure_signed(origin)?;1241            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12421243            let mut target_collection = <Collection<T>>::get(collection_id);1244            target_collection.offchain_schema = schema;1245            <Collection<T>>::insert(collection_id, target_collection);12461247            Ok(())1248        }12491250        /// Set const on-chain data schema.1251        /// 1252        /// # Permissions1253        /// 1254        /// * Collection Owner1255        /// * Collection Admin1256        /// 1257        /// # Arguments1258        /// 1259        /// * collection_id.1260        /// 1261        /// * schema: String representing the const on-chain data schema.1262        #[weight = T::WeightInfo::set_const_on_chain_schema()]1263        pub fn set_const_on_chain_schema (1264            origin,1265            collection_id: CollectionId,1266            schema: Vec<u8>1267        ) -> DispatchResult {1268            let sender = ensure_signed(origin)?;1269            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12701271            let mut target_collection = <Collection<T>>::get(collection_id);1272            target_collection.const_on_chain_schema = schema;1273            <Collection<T>>::insert(collection_id, target_collection);12741275            Ok(())1276        }12771278        /// Set variable on-chain data schema.1279        /// 1280        /// # Permissions1281        /// 1282        /// * Collection Owner1283        /// * Collection Admin1284        /// 1285        /// # Arguments1286        /// 1287        /// * collection_id.1288        /// 1289        /// * schema: String representing the variable on-chain data schema.1290        #[weight = T::WeightInfo::set_const_on_chain_schema()]1291        pub fn set_variable_on_chain_schema (1292            origin,1293            collection_id: CollectionId,1294            schema: Vec<u8>1295        ) -> DispatchResult {1296            let sender = ensure_signed(origin)?;1297            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12981299            let mut target_collection = <Collection<T>>::get(collection_id);1300            target_collection.variable_on_chain_schema = schema;1301            <Collection<T>>::insert(collection_id, target_collection);13021303            Ok(())1304        }13051306        // Sudo permissions function1307        #[weight = 0]1308        pub fn set_chain_limits(1309            origin,1310            limits: ChainLimits1311        ) -> DispatchResult {1312            ensure_root(origin)?;1313            <ChainLimit>::put(limits);1314            Ok(())1315        }13161317        /// Enable smart contract self-sponsoring.1318        /// 1319        /// # Permissions1320        /// 1321        /// * Contract Owner1322        /// 1323        /// # Arguments1324        /// 1325        /// * contract address1326        /// * enable flag1327        /// 1328        #[weight = T::WeightInfo::enable_contract_sponsoring()]1329        pub fn enable_contract_sponsoring(1330            origin,1331            contract_address: T::AccountId,1332            enable: bool1333        ) -> DispatchResult {13341335            let sender = ensure_signed(origin)?;13361337            #[cfg(feature = "runtime-benchmarks")]1338            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13391340            let mut is_owner = false;1341            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1342                let owner = <ContractOwner<T>>::get(&contract_address);1343                is_owner = sender == owner;1344            }1345            ensure!(is_owner, Error::<T>::NoPermission);13461347            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1348            Ok(())1349        }13501351        /// Set the rate limit for contract sponsoring to specified number of blocks.1352        /// 1353        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1354        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1355        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1356        /// from contract endowment if there are at least B blocks between such transactions. 1357        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1358        /// 1359        /// # Permissions1360        /// 1361        /// * Contract Owner1362        /// 1363        /// # Arguments1364        /// 1365        /// -`contract_address`: Address of the contract to sponsor1366        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1367        /// 1368        #[weight = 0]1369        pub fn set_contract_sponsoring_rate_limit(1370            origin,1371            contract_address: T::AccountId,1372            rate_limit: T::BlockNumber1373        ) -> DispatchResult {1374            let sender = ensure_signed(origin)?;1375            let mut is_owner = false;1376            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1377                let owner = <ContractOwner<T>>::get(&contract_address);1378                is_owner = sender == owner;1379            }1380            ensure!(is_owner, Error::<T>::NoPermission);13811382            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1383            Ok(())1384        }13851386        // #[cfg(feature = "runtime-benchmarks")]1387        // #[weight = 0]1388        // pub fn add_contract_sponsoring_debug(1389        //     origin,1390        //     contract_address: T::AccountId, 1391        //     owner: T::AccountId) -> DispatchResult {1392        //     let sender = ensure_signed(origin)?;1393        //     <ContractOwner<T>>::insert(contract_address.clone(), owner);1394        //     Ok(())1395        // }1396    1397    }1398}13991400impl<T: Trait> Module<T> {14011402    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {14031404        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1405            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1406            Self::check_white_list(collection_id, owner)?;1407            Self::check_white_list(collection_id, sender)?;1408        }14091410        Ok(())1411    }14121413    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1414        match target_collection.mode1415        {1416            CollectionMode::NFT => {1417                if let CreateItemData::NFT(data) = data {1418                    // check sizes1419                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1420                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1421                } else {1422                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1423                }1424            },1425            CollectionMode::Fungible(_) => {1426                if let CreateItemData::Fungible(_) = data {1427                } else {1428                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1429                }1430            },1431            CollectionMode::ReFungible(_) => {1432                if let CreateItemData::ReFungible(data) = data {14331434                    // check sizes1435                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1436                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1437                } else {1438                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1439                }1440            },1441            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1442        };14431444        Ok(())1445    }14461447    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1448        match data1449        {1450            CreateItemData::NFT(data) => {1451                let item = NftItemType {1452                    collection: collection_id,1453                    owner,1454                    const_data: data.const_data,1455                    variable_data: data.variable_data1456                };14571458                Self::add_nft_item(item)?;1459            },1460            CreateItemData::Fungible(_) => {1461                let item = FungibleItemType {1462                    collection: collection_id,1463                    owner,1464                    value: (10 as u128).pow(collection.decimal_points)1465                };14661467                Self::add_fungible_item(item)?;1468            },1469            CreateItemData::ReFungible(data) => {1470                let mut owner_list = Vec::new();1471                let value = (10 as u128).pow(collection.decimal_points);1472                owner_list.push(Ownership {owner: owner.clone(), fraction: value});14731474                let item = ReFungibleItemType {1475                    collection: collection_id,1476                    owner: owner_list,1477                    const_data: data.const_data,1478                    variable_data: data.variable_data1479                };14801481                Self::add_refungible_item(item)?;1482            }1483        };148414851486        // call event1487        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));14881489        Ok(())1490    }14911492    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1493        let current_index = <ItemListIndex>::get(item.collection)1494            .checked_add(1)1495            .ok_or(Error::<T>::NumOverflow)?;1496        let itemcopy = item.clone();1497        let owner = item.owner.clone();1498        let value = item.value as u64;14991500        Self::add_token_index(item.collection, current_index, owner.clone())?;15011502        <ItemListIndex>::insert(item.collection, current_index);1503        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15041505        // Add current block1506        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1507        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1508        1509        // Update balance1510        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1511            .checked_add(value)1512            .ok_or(Error::<T>::NumOverflow)?;1513        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15141515        Ok(())1516    }15171518    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1519        let current_index = <ItemListIndex>::get(item.collection)1520            .checked_add(1)1521            .ok_or(Error::<T>::NumOverflow)?;1522        let itemcopy = item.clone();15231524        let value = item.owner.first().unwrap().fraction as u64;1525        let owner = item.owner.first().unwrap().owner.clone();15261527        Self::add_token_index(item.collection, current_index, owner.clone())?;15281529        <ItemListIndex>::insert(item.collection, current_index);1530        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15311532        // Add current block1533        let block_number: T::BlockNumber = 0.into();1534        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);15351536        // Update balance1537        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1538            .checked_add(value)1539            .ok_or(Error::<T>::NumOverflow)?;1540        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15411542        Ok(())1543    }15441545    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1546        let current_index = <ItemListIndex>::get(item.collection)1547            .checked_add(1)1548            .ok_or(Error::<T>::NumOverflow)?;15491550        let item_owner = item.owner.clone();1551        let collection_id = item.collection.clone();1552        Self::add_token_index(collection_id, current_index, item.owner.clone())?;15531554        <ItemListIndex>::insert(collection_id, current_index);1555        <NftItemList<T>>::insert(collection_id, current_index, item);15561557        // Add current block1558        let block_number: T::BlockNumber = 0.into();1559        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);15601561        // Update balance1562        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1563            .checked_add(1)1564            .ok_or(Error::<T>::NumOverflow)?;1565        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);15661567        Ok(())1568    }15691570    fn burn_refungible_item(1571        collection_id: CollectionId,1572        item_id: TokenId,1573        owner: T::AccountId,1574    ) -> DispatchResult {1575        ensure!(1576            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1577            Error::<T>::TokenNotFound1578        );1579        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1580        let item = collection1581            .owner1582            .iter()1583            .filter(|&i| i.owner == owner)1584            .next()1585            .unwrap();1586        Self::remove_token_index(collection_id, item_id, owner.clone())?;15871588        // remove approve list1589        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));15901591        // update balance1592        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1593            .checked_sub(item.fraction as u64)1594            .ok_or(Error::<T>::NumOverflow)?;1595        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15961597        <ReFungibleItemList<T>>::remove(collection_id, item_id);15981599        Ok(())1600    }16011602    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1603        ensure!(1604            <NftItemList<T>>::contains_key(collection_id, item_id),1605            Error::<T>::TokenNotFound1606        );1607        let item = <NftItemList<T>>::get(collection_id, item_id);1608        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16091610        // remove approve list1611        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16121613        // update balance1614        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1615            .checked_sub(1)1616            .ok_or(Error::<T>::NumOverflow)?;1617        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1618        <NftItemList<T>>::remove(collection_id, item_id);16191620        Ok(())1621    }16221623    fn burn_fungible_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1624        ensure!(1625            <FungibleItemList<T>>::contains_key(collection_id, item_id),1626            Error::<T>::TokenNotFound1627        );1628        let item = <FungibleItemList<T>>::get(collection_id, item_id);1629        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16301631        // remove approve list1632        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16331634        // update balance1635        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1636            .checked_sub(item.value as u64)1637            .ok_or(Error::<T>::NumOverflow)?;1638        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);16391640        <FungibleItemList<T>>::remove(collection_id, item_id);16411642        Ok(())1643    }16441645    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1646        ensure!(1647            <Collection<T>>::contains_key(collection_id),1648            Error::<T>::CollectionNotFound1649        );1650        Ok(())1651    }16521653    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1654        Self::collection_exists(collection_id)?;16551656        let target_collection = <Collection<T>>::get(collection_id);1657        ensure!(1658            subject == target_collection.owner,1659            Error::<T>::NoPermission1660        );16611662        Ok(())1663    }16641665    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1666        let target_collection = <Collection<T>>::get(collection_id);1667        let mut result: bool = subject == target_collection.owner;1668        let exists = <AdminList<T>>::contains_key(collection_id);16691670        if !result & exists {1671            if <AdminList<T>>::get(collection_id).contains(&subject) {1672                result = true1673            }1674        }16751676        result1677    }16781679    fn check_owner_or_admin_permissions(1680        collection_id: CollectionId,1681        subject: T::AccountId,1682    ) -> DispatchResult {1683        Self::collection_exists(collection_id)?;1684        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());16851686        ensure!(1687            result,1688            Error::<T>::NoPermission1689        );1690        Ok(())1691    }16921693    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1694        let target_collection = <Collection<T>>::get(collection_id);16951696        match target_collection.mode {1697            CollectionMode::NFT => {1698                <NftItemList<T>>::get(collection_id, item_id).owner == subject1699            }1700            CollectionMode::Fungible(_) => {1701                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1702            }1703            CollectionMode::ReFungible(_) => {1704                <ReFungibleItemList<T>>::get(collection_id, item_id)1705                    .owner1706                    .iter()1707                    .any(|i| i.owner == subject)1708            }1709            CollectionMode::Invalid => false,1710        }1711    }17121713    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1714        let mes = Error::<T>::AddresNotInWhiteList;1715        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1716        let wl = <WhiteList<T>>::get(collection_id);1717        ensure!(wl.contains(address), mes);17181719        Ok(())1720    }17211722    fn transfer_fungible(1723        collection_id: CollectionId,1724        item_id: TokenId,1725        value: u64,1726        owner: T::AccountId,1727        new_owner: T::AccountId,1728    ) -> DispatchResult {1729        ensure!(1730            <FungibleItemList<T>>::contains_key(collection_id, item_id),1731            Error::<T>::TokenNotFound1732        );17331734        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1735        let amount = full_item.value;17361737        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);17381739        // update balance1740        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1741            .checked_sub(value)1742            .ok_or(Error::<T>::NumOverflow)?;1743        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);17441745        let mut new_owner_account_id = 0;1746        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1747        if new_owner_items.len() > 0 {1748            new_owner_account_id = new_owner_items[0];1749        }17501751        let val64 = value.into();17521753        // transfer1754        if amount == val64 && new_owner_account_id == 0 {1755            // change owner1756            // new owner do not have account1757            let mut new_full_item = full_item.clone();1758            new_full_item.owner = new_owner.clone();1759            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17601761            // update balance1762            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1763                .checked_add(value)1764                .ok_or(Error::<T>::NumOverflow)?;1765            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17661767            // update index collection1768            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1769        } else {1770            let mut new_full_item = full_item.clone();1771            new_full_item.value -= val64;17721773            // separate amount1774            if new_owner_account_id > 0 {1775                // new owner has account1776                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1777                item.value += val64;17781779                // update balance1780                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1781                    .checked_add(value)1782                    .ok_or(Error::<T>::NumOverflow)?;1783                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17841785                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1786            } else {1787                // new owner do not have account1788                let item = FungibleItemType {1789                    collection: collection_id,1790                    owner: new_owner.clone(),1791                    value: val64,1792                };17931794                Self::add_fungible_item(item)?;1795            }17961797            if amount == val64 {1798                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;17991800                // remove approve list1801                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1802                <FungibleItemList<T>>::remove(collection_id, item_id);1803            }18041805            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1806        }18071808        Ok(())1809    }18101811    fn transfer_refungible(1812        collection_id: CollectionId,1813        item_id: TokenId,1814        value: u64,1815        owner: T::AccountId,1816        new_owner: T::AccountId,1817    ) -> DispatchResult {1818        ensure!(1819            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1820            Error::<T>::TokenNotFound1821        );18221823        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1824        let item = full_item1825            .owner1826            .iter()1827            .filter(|i| i.owner == owner)1828            .next()1829            .ok_or(Error::<T>::NumOverflow)?;1830        let amount = item.fraction;18311832        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);18331834        // update balance1835        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1836            .checked_sub(value)1837            .ok_or(Error::<T>::NumOverflow)?;1838        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18391840        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1841            .checked_add(value)1842            .ok_or(Error::<T>::NumOverflow)?;1843        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18441845        let old_owner = item.owner.clone();1846        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1847        let val64 = value.into();18481849        // transfer1850        if amount == val64 && !new_owner_has_account {1851            // change owner1852            // new owner do not have account1853            let mut new_full_item = full_item.clone();1854            new_full_item1855                .owner1856                .iter_mut()1857                .find(|i| i.owner == owner)1858                .unwrap()1859                .owner = new_owner.clone();1860            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18611862            // update index collection1863            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1864        } else {1865            let mut new_full_item = full_item.clone();1866            new_full_item1867                .owner1868                .iter_mut()1869                .find(|i| i.owner == owner)1870                .unwrap()1871                .fraction -= val64;18721873            // separate amount1874            if new_owner_has_account {1875                // new owner has account1876                new_full_item1877                    .owner1878                    .iter_mut()1879                    .find(|i| i.owner == new_owner)1880                    .unwrap()1881                    .fraction += val64;1882            } else {1883                // new owner do not have account1884                new_full_item.owner.push(Ownership {1885                    owner: new_owner.clone(),1886                    fraction: val64,1887                });1888                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1889            }18901891            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1892        }18931894        Ok(())1895    }18961897    fn transfer_nft(1898        collection_id: CollectionId,1899        item_id: TokenId,1900        sender: T::AccountId,1901        new_owner: T::AccountId,1902    ) -> DispatchResult {1903        ensure!(1904            <NftItemList<T>>::contains_key(collection_id, item_id),1905            Error::<T>::TokenNotFound1906        );19071908        let mut item = <NftItemList<T>>::get(collection_id, item_id);19091910        ensure!(1911            sender == item.owner,1912            Error::<T>::MustBeTokenOwner1913        );19141915        // update balance1916        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1917            .checked_sub(1)1918            .ok_or(Error::<T>::NumOverflow)?;1919        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19201921        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1922            .checked_add(1)1923            .ok_or(Error::<T>::NumOverflow)?;1924        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19251926        // change owner1927        let old_owner = item.owner.clone();1928        item.owner = new_owner.clone();1929        <NftItemList<T>>::insert(collection_id, item_id, item);19301931        // update index collection1932        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;19331934        // reset approved list1935        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1936        Ok(())1937    }1938    1939    fn item_exists(1940        collection_id: CollectionId,1941        item_id: TokenId,1942        mode: &CollectionMode1943    ) -> DispatchResult {1944        match mode {1945            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1946            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1947            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1948            _ => ()1949        };1950        1951        Ok(())1952    }19531954    fn set_re_fungible_variable_data(1955        collection_id: CollectionId,1956        item_id: TokenId,1957        data: Vec<u8>1958    ) -> DispatchResult {1959        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);19601961        item.variable_data = data;19621963        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);19641965        Ok(())1966    }19671968    fn set_nft_variable_data(1969        collection_id: CollectionId,1970        item_id: TokenId,1971        data: Vec<u8>1972    ) -> DispatchResult {1973        let mut item = <NftItemList<T>>::get(collection_id, item_id);1974        1975        item.variable_data = data;19761977        <NftItemList<T>>::insert(collection_id, item_id, item);1978        1979        Ok(())1980    }19811982    fn init_collection(item: &CollectionType<T::AccountId>) {1983        // check params1984        assert!(1985            item.decimal_points <= 4,1986            "decimal_points parameter must be lower than 4"1987        );1988        assert!(1989            item.name.len() <= 64,1990            "Collection name can not be longer than 63 char"1991        );1992        assert!(1993            item.name.len() <= 256,1994            "Collection description can not be longer than 255 char"1995        );1996        assert!(1997            item.token_prefix.len() <= 16,1998            "Token prefix can not be longer than 15 char"1999        );20002001        // Generate next collection ID2002        let next_id = CreatedCollectionCount::get()2003            .checked_add(1)2004            .unwrap();20052006        CreatedCollectionCount::put(next_id);2007    }20082009    fn init_nft_token(item: &NftItemType<T::AccountId>) {2010        let current_index = <ItemListIndex>::get(item.collection)2011            .checked_add(1)2012            .unwrap();20132014        let item_owner = item.owner.clone();2015        let collection_id = item.collection.clone();2016        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();20172018        <ItemListIndex>::insert(collection_id, current_index);20192020        // Update balance2021        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2022            .checked_add(1)2023            .unwrap();2024        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2025    }20262027    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2028        let current_index = <ItemListIndex>::get(item.collection)2029            .checked_add(1)2030            .unwrap();2031        let owner = item.owner.clone();2032        let value = item.value as u64;20332034        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20352036        <ItemListIndex>::insert(item.collection, current_index);20372038        // Update balance2039        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2040            .checked_add(value)2041            .unwrap();2042        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2043    }20442045    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2046        let current_index = <ItemListIndex>::get(item.collection)2047            .checked_add(1)2048            .unwrap();20492050        let value = item.owner.first().unwrap().fraction as u64;2051        let owner = item.owner.first().unwrap().owner.clone();20522053        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20542055        <ItemListIndex>::insert(item.collection, current_index);20562057        // Update balance2058        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2059            .checked_add(value)2060            .unwrap();2061        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2062    }20632064    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {20652066        // add to account limit2067        if <AccountItemCount<T>>::contains_key(owner.clone()) {20682069            // bound Owned tokens by a single address2070            let count = <AccountItemCount<T>>::get(owner.clone());2071            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);20722073            <AccountItemCount<T>>::insert(owner.clone(), count2074                .checked_add(1)2075                .ok_or(Error::<T>::NumOverflow)?);2076        }2077        else {2078            <AccountItemCount<T>>::insert(owner.clone(), 1);2079        }20802081        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2082        if list_exists {2083            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2084            let item_contains = list.contains(&item_index.clone());20852086            if !item_contains {2087                list.push(item_index.clone());2088            }20892090            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2091        } else {2092            let mut itm = Vec::new();2093            itm.push(item_index.clone());2094            <AddressTokens<T>>::insert(collection_id, owner, itm);2095            2096        }20972098        Ok(())2099    }21002101    fn remove_token_index(2102        collection_id: CollectionId,2103        item_index: TokenId,2104        owner: T::AccountId,2105    ) -> DispatchResult {21062107        // update counter2108        <AccountItemCount<T>>::insert(owner.clone(), 2109            <AccountItemCount<T>>::get(owner.clone())2110            .checked_sub(1)2111            .ok_or(Error::<T>::NumOverflow)?);211221132114        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2115        if list_exists {2116            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2117            let item_contains = list.contains(&item_index.clone());21182119            if item_contains {2120                list.retain(|&item| item != item_index);2121                <AddressTokens<T>>::insert(collection_id, owner, list);2122            }2123        }21242125        Ok(())2126    }21272128    fn move_token_index(2129        collection_id: CollectionId,2130        item_index: TokenId,2131        old_owner: T::AccountId,2132        new_owner: T::AccountId,2133    ) -> DispatchResult {2134        Self::remove_token_index(collection_id, item_index, old_owner)?;2135        Self::add_token_index(collection_id, item_index, new_owner)?;21362137        Ok(())2138    }2139}21402141////////////////////////////////////////////////////////////////////////////////////////////////////2142// Economic models2143// #region21442145/// Fee multiplier.2146pub type Multiplier = FixedU128;21472148type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2149    <T as system::Trait>::AccountId,2150>>::Balance;2151type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2152    <T as system::Trait>::AccountId,2153>>::NegativeImbalance;21542155/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2156/// in the queue.2157#[derive(Encode, Decode, Clone, Eq, PartialEq)]2158pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2159    #[codec(compact)] BalanceOf<T>2160);21612162impl<T: Trait + Send + Sync> sp_std::fmt::Debug2163    for ChargeTransactionPayment<T>2164{2165    #[cfg(feature = "std")]2166    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2167        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2168    }2169    #[cfg(not(feature = "std"))]2170    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2171        Ok(())2172    }2173}21742175impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2176where2177    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2178    BalanceOf<T>: Send + Sync + FixedPointOperand,2179{2180    /// utility constructor. Used only in client/factory code.2181    pub fn from(fee: BalanceOf<T>) -> Self {2182        Self(fee)2183    }21842185    pub fn traditional_fee(2186        len: usize,2187        info: &DispatchInfoOf<T::Call>,2188        tip: BalanceOf<T>,2189    ) -> BalanceOf<T>2190    where2191        T::Call: Dispatchable<Info = DispatchInfo>,2192    {2193        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2194    }21952196    fn withdraw_fee(2197        &self,2198        who: &T::AccountId,2199        call: &T::Call,2200        info: &DispatchInfoOf<T::Call>,2201        len: usize,2202    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2203        let tip = self.0;22042205        // Set fee based on call type. Creating collection costs 1 Unique.2206        // All other transactions have traditional fees so far2207        // let fee = match call.is_sub_type() {2208        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2209        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2210        //                                                 // _ => <BalanceOf<T>>::from(100)2211        // };2212        let fee = Self::traditional_fee(len, info, tip);22132214        // Determine who is paying transaction fee based on ecnomic model2215        // Parse call to extract collection ID and access collection sponsor2216        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2217            Some(Call::create_item(collection_id, _properties, _owner)) => {2218                <Collection<T>>::get(collection_id).sponsor2219            }2220            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2221                let _collection_mode = <Collection<T>>::get(collection_id).mode;22222223                // sponsor timeout2224                let sponsor_transfer = match _collection_mode {2225                    CollectionMode::NFT => {2226                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2227                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2228                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2229                        if block_number >= limit_time {2230                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2231                            true2232                        }2233                        else {2234                            false2235                        }2236                    }2237                    CollectionMode::Fungible(_) => {2238                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2239                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2240                        if basket.iter().any(|i| i.address == _new_owner.clone())2241                        {2242                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2243                            let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2244                            if block_number >= limit_time {2245                                basket.retain(|x| x.address == item.address);2246                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2247                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2248                                true2249                            }2250                            else {2251                                false2252                            }2253                        }2254                        else {2255                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2256                            true2257                        }2258                    }2259                    CollectionMode::ReFungible(_) => {2260                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2261                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2262                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2263                        if block_number >= limit_time {2264                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2265                            true2266                        } else {2267                            false2268                        }2269                    }2270                    _ => {2271                        false2272                    },2273                };22742275                if !sponsor_transfer {2276                    T::AccountId::default()2277                } else {2278                    <Collection<T>>::get(collection_id).sponsor2279                }2280            }22812282            _ => T::AccountId::default(),2283        };22842285        // Sponsor smart contracts2286        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {22872288            // On instantiation: set the contract owner2289            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {22902291                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2292                    code_hash,2293                    &data,2294                    &who,2295                );2296                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());22972298                T::AccountId::default()2299            },23002301            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2302            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {23032304                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());23052306                let mut sponsor_transfer = false;2307                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2308                    let last_tx_block = <ContractSponsorBasket<T>>::get(&called_contract);2309                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2310                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2311                    let limit_time = last_tx_block + rate_limit;23122313                    if block_number >= limit_time {2314                        <ContractSponsorBasket<T>>::insert(called_contract.clone(), block_number);2315                        sponsor_transfer = true;2316                    }2317                } else {2318                    sponsor_transfer = false;2319                }2320               2321                2322                let mut sp = T::AccountId::default();2323                if sponsor_transfer {2324                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2325                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2326                            sp = called_contract;2327                        }2328                    }2329                }23302331                sp2332            },23332334            _ => sponsor,2335        };23362337        let mut who_pays_fee: T::AccountId = sponsor.clone();2338        if sponsor == T::AccountId::default() {2339            who_pays_fee = who.clone();2340        }23412342        // Only mess with balances if fee is not zero.2343        if fee.is_zero() {2344            return Ok((fee, None));2345        }23462347        match <T as transaction_payment::Trait>::Currency::withdraw(2348            &who_pays_fee,2349            fee,2350            if tip.is_zero() {2351                WithdrawReason::TransactionPayment.into()2352            } else {2353                WithdrawReason::TransactionPayment | WithdrawReason::Tip2354            },2355            ExistenceRequirement::KeepAlive,2356        ) {2357            Ok(imbalance) => Ok((fee, Some(imbalance))),2358            Err(_) => Err(InvalidTransaction::Payment.into()),2359        }2360    }2361}236223632364impl<T: Trait + Send + Sync> SignedExtension2365    for ChargeTransactionPayment<T>2366where2367    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2368    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2369{2370    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2371    type AccountId = T::AccountId;2372    type Call = T::Call;2373    type AdditionalSigned = ();2374    type Pre = (2375        BalanceOf<T>,2376        Self::AccountId,2377        Option<NegativeImbalanceOf<T>>,2378        BalanceOf<T>,2379    );2380    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2381        Ok(())2382    }23832384    fn validate(2385        &self,2386        _who: &Self::AccountId,2387        _call: &Self::Call,2388        _info: &DispatchInfoOf<Self::Call>,2389        _len: usize,2390    ) -> TransactionValidity {2391        Ok(ValidTransaction::default())2392    }23932394    fn pre_dispatch(2395        self,2396        who: &Self::AccountId,2397        call: &Self::Call,2398        info: &DispatchInfoOf<Self::Call>,2399        len: usize,2400    ) -> Result<Self::Pre, TransactionValidityError> {2401        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2402        Ok((self.0, who.clone(), imbalance, fee))2403    }24042405    fn post_dispatch(2406        pre: Self::Pre,2407        info: &DispatchInfoOf<Self::Call>,2408        post_info: &PostDispatchInfoOf<Self::Call>,2409        len: usize,2410        _result: &DispatchResult,2411    ) -> Result<(), TransactionValidityError> {2412        let (tip, who, imbalance, fee) = pre;2413        if let Some(payed) = imbalance {2414            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2415                len as u32, info, post_info, tip,2416            );2417            let refund = fee.saturating_sub(actual_fee);2418            let actual_payment =2419                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2420                    &who, refund,2421                ) {2422                    Ok(refund_imbalance) => {2423                        // The refund cannot be larger than the up front payed max weight.2424                        // `PostDispatchInfo::calc_unspent` guards against such a case.2425                        match payed.offset(refund_imbalance) {2426                            Ok(actual_payment) => actual_payment,2427                            Err(_) => return Err(InvalidTransaction::Payment.into()),2428                        }2429                    }2430                    // We do not recreate the account using the refund. The up front payment2431                    // is gone in that case.2432                    Err(_) => payed,2433                };2434            let imbalances = actual_payment.split(tip);2435            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2436                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2437            );2438        }2439        Ok(())2440    }2441}24422443// #endregion
after · pallets/nft/src/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]23#[cfg(feature = "std")]4pub use std::*;56#[cfg(feature = "std")]7pub use serde::*;89use codec::{Decode, Encode};10pub use frame_support::{11    construct_runtime, decl_event, decl_module, decl_storage, decl_error,12    dispatch::DispatchResult,13    ensure, fail, parameter_types,14    traits::{15        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,16        Randomness, WithdrawReason,17    },18    weights::{19        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},20        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,21        WeightToFeePolynomial,22    },23    IsSubType, StorageValue,24};2526use frame_system::{self as system, ensure_signed, ensure_root};27use sp_runtime::sp_std::prelude::Vec;28use sp_runtime::{29    traits::{30        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SignedExtension, Zero,31    },32    transaction_validity::{33        InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,34    },35    FixedPointOperand, FixedU128,36};37use pallet_contracts::ContractAddressFor;38use sp_runtime::traits::StaticLookup;3940#[cfg(test)]41mod mock;4243#[cfg(test)]44mod tests;4546mod default_weights;4748pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;4950// Structs51// #region5253pub type CollectionId = u32;54pub type TokenId = u32;5556pub type DecimalPoints = u8;5758#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]59#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]60pub enum CollectionMode {61    Invalid,62    NFT,63    // decimal points64    Fungible(DecimalPoints),65    // decimal points66    ReFungible(DecimalPoints),67}6869impl Into<u8> for CollectionMode {70    fn into(self) -> u8 {71        match self {72            CollectionMode::Invalid => 0,73            CollectionMode::NFT => 1,74            CollectionMode::Fungible(_) => 2,75            CollectionMode::ReFungible(_) => 3,76        }77    }78}7980#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]81#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]82pub enum AccessMode {83    Normal,84    WhiteList,85}86impl Default for AccessMode {87    fn default() -> Self {88        Self::Normal89    }90}9192impl Default for CollectionMode {93    fn default() -> Self {94        Self::Invalid95    }96}9798#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]99#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]100pub struct Ownership<AccountId> {101    pub owner: AccountId,102    pub fraction: u128,103}104105#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]106#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]107pub struct CollectionType<AccountId> {108    pub owner: AccountId,109    pub mode: CollectionMode,110    pub access: AccessMode,111    pub decimal_points: DecimalPoints,112    pub name: Vec<u16>,        // 64 include null escape char113    pub description: Vec<u16>, // 256 include null escape char114    pub token_prefix: Vec<u8>, // 16 include null escape char115    pub mint_mode: bool,116    pub offchain_schema: Vec<u8>,117    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender118    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship119    pub variable_on_chain_schema: Vec<u8>, //120    pub const_on_chain_schema: Vec<u8>, //121}122123#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]124#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]125pub struct NftItemType<AccountId> {126    pub collection: CollectionId,127    pub owner: AccountId,128    pub const_data: Vec<u8>,129    pub variable_data: Vec<u8>,130}131132#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]133#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]134pub struct FungibleItemType<AccountId> {135    pub collection: CollectionId,136    pub owner: AccountId,137    pub value: u128,138}139140#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]141#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]142pub struct ReFungibleItemType<AccountId> {143    pub collection: CollectionId,144    pub owner: Vec<Ownership<AccountId>>,145    pub const_data: Vec<u8>,146    pub variable_data: Vec<u8>,147}148149#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]150#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]151pub struct ApprovePermissions<AccountId> {152    pub approved: AccountId,153    pub amount: u64,154}155156#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]157#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]158pub struct VestingItem<AccountId, Moment> {159    pub sender: AccountId,160    pub recipient: AccountId,161    pub collection_id: CollectionId,162    pub item_id: TokenId,163    pub amount: u64,164    pub vesting_date: Moment,165}166167#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]168#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169pub struct BasketItem<AccountId, BlockNumber> {170    pub address: AccountId,171    pub start_block: BlockNumber,172}173174#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]175#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]176pub struct ChainLimits {177    pub collection_numbers_limit: u32,178    pub account_token_ownership_limit: u32,179    pub collections_admins_limit: u64,180    pub custom_data_limit: u32,181182    // Timeouts for item types in passed blocks183    pub nft_sponsor_transfer_timeout: u32,184    pub fungible_sponsor_transfer_timeout: u32,185    pub refungible_sponsor_transfer_timeout: u32,186}187188pub trait WeightInfo {189	fn create_collection() -> Weight;190	fn destroy_collection() -> Weight;191	fn add_to_white_list() -> Weight;192	fn remove_from_white_list() -> Weight;193    fn set_public_access_mode() -> Weight;194    fn set_mint_permission() -> Weight;195    fn change_collection_owner() -> Weight;196    fn add_collection_admin() -> Weight;197    fn remove_collection_admin() -> Weight;198    fn set_collection_sponsor() -> Weight;199    fn confirm_sponsorship() -> Weight;200    fn remove_collection_sponsor() -> Weight;201    fn create_item(s: usize) -> Weight;202    fn burn_item() -> Weight;203    fn transfer() -> Weight;204    fn approve() -> Weight;205    fn transfer_from() -> Weight;206    fn set_offchain_schema() -> Weight;207    fn set_const_on_chain_schema() -> Weight;208    fn set_variable_on_chain_schema() -> Weight;209    fn set_variable_meta_data() -> Weight;210    fn enable_contract_sponsoring() -> Weight;211}212213#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]214#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]215pub struct CreateNftData {216    pub const_data: Vec<u8>,217    pub variable_data: Vec<u8>,218}219220#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]221#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]222pub struct CreateFungibleData {223}224225#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]226#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]227pub struct CreateReFungibleData {228    pub const_data: Vec<u8>,229    pub variable_data: Vec<u8>,230}231232#[derive(Encode, Decode, Debug, Clone, PartialEq)]233#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]234pub enum CreateItemData {235    NFT(CreateNftData),236    Fungible(CreateFungibleData),237    ReFungible(CreateReFungibleData)238}239240impl CreateItemData {241    pub fn len(&self) -> usize {242        let len = match self {243            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),244            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),245            _ => 0246        };247        248        return len;249    }250}251252impl From<CreateNftData> for CreateItemData {253    fn from(item: CreateNftData) -> Self {254        CreateItemData::NFT(item)255    }256}257258impl From<CreateReFungibleData> for CreateItemData {259    fn from(item: CreateReFungibleData) -> Self {260        CreateItemData::ReFungible(item)261    }262}263264impl From<CreateFungibleData> for CreateItemData {265    fn from(item: CreateFungibleData) -> Self {266        CreateItemData::Fungible(item)267    }268}269270271decl_error! {272	/// Error for non-fungible-token module.273	pub enum Error for Module<T: Trait> {274        /// Total collections bound exceeded.275        TotalCollectionsLimitExceeded,276		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.277        CollectionDecimalPointLimitExceeded, 278        /// Collection name can not be longer than 63 char.279        CollectionNameLimitExceeded, 280        /// Collection description can not be longer than 255 char.281        CollectionDescriptionLimitExceeded, 282        /// Token prefix can not be longer than 15 char.283        CollectionTokenPrefixLimitExceeded,284        /// This collection does not exist.285        CollectionNotFound,286        /// Item not exists.287        TokenNotFound,288        /// Arithmetic calculation overflow.289        NumOverflow,       290        /// Account already has admin role.291        AlreadyAdmin,  292        /// You do not own this collection.293        NoPermission,294        /// This address is not set as sponsor, use setCollectionSponsor first.295        ConfirmUnsetSponsorFail,296        /// Collection is not in mint mode.297        PublicMintingNotAllowed,298        /// Sender parameter and item owner must be equal.299        MustBeTokenOwner,300        /// Item balance not enough.301        TokenValueTooLow,302        /// Size of item is too large.303        NftSizeLimitExceeded,304        /// No approve found305        ApproveNotFound,306        /// Requested value more than approved.307        TokenValueNotEnough,308        /// Only approved addresses can call this method.309        ApproveRequired,310        /// Address is not in white list.311        AddresNotInWhiteList,312        /// Number of collection admins bound exceeded.313        CollectionAdminsLimitExceeded,314        /// Owned tokens by a single address bound exceeded.315        AddressOwnershipLimitExceeded,316        /// Length of items properties must be greater than 0.317        EmptyArgument,318        /// const_data exceeded data limit.319        TokenConstDataLimitExceeded,320        /// variable_data exceeded data limit.321        TokenVariableDataLimitExceeded,322        /// Not NFT item data used to mint in NFT collection.323        NotNftDataUsedToMintNftCollectionToken,324        /// Not Fungible item data used to mint in Fungible collection.325        NotFungibleDataUsedToMintFungibleCollectionToken,326        /// Not Re Fungible item data used to mint in Re Fungible collection.327        NotReFungibleDataUsedToMintReFungibleCollectionToken,328        /// Unexpected collection type.329        UnexpectedCollectionType,330        /// Can't store metadata in fungible tokens.331        CantStoreMetadataInFungibleTokens332	}333}334335pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {336    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;337338    /// Weight information for extrinsics in this pallet.339	type WeightInfo: WeightInfo;340}341342#[cfg(feature = "runtime-benchmarks")]343mod benchmarking;344345// #endregion346347decl_storage! {348    trait Store for Module<T: Trait> as Nft {349350        // Private members351        NextCollectionID: CollectionId;352        CreatedCollectionCount: u32;353        ChainVersion: u64;354        ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;355356        // Chain limits struct357        pub ChainLimit get(fn chain_limit) config(): ChainLimits;358359        // Bound counters360        CollectionCount: u32;361        pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u32;362363        // Basic collections364        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;365        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;366        pub WhiteList get(fn white_list): map hasher(identity) CollectionId => Vec<T::AccountId>;367368        /// Balance owner per collection map369        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => u64;370371        /// second parameter: item id + owner account id372        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;373374        /// Item collections375        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => NftItemType<T::AccountId>;376        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => FungibleItemType<T::AccountId>;377        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => ReFungibleItemType<T::AccountId>;378379        /// Index list380        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;381382        /// Tokens transfer baskets383        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;384        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Vec<BasketItem<T::AccountId, T::BlockNumber>>;385        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;386387        // Contract Sponsorship and Ownership388        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;389        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;390        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;391        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;392    }393    add_extra_genesis {394        build(|config: &GenesisConfig<T>| {395            // Modification of storage396            for (_num, _c) in &config.collection {397                <Module<T>>::init_collection(_c);398            }399400            for (_num, _q, _i) in &config.nft_item_id {401                <Module<T>>::init_nft_token(_i);402            }403404            for (_num, _q, _i) in &config.fungible_item_id {405                <Module<T>>::init_fungible_token(_i);406            }407408            for (_num, _q, _i) in &config.refungible_item_id {409                <Module<T>>::init_refungible_token(_i);410            }411        })412    }413}414415decl_event!(416    pub enum Event<T>417    where418        AccountId = <T as system::Trait>::AccountId,419    {420        /// New collection was created421        /// 422        /// # Arguments423        /// 424        /// * collection_id: Globally unique identifier of newly created collection.425        /// 426        /// * mode: [CollectionMode] converted into u8.427        /// 428        /// * account_id: Collection owner.429        Created(CollectionId, u8, AccountId),430431        /// New item was created.432        /// 433        /// # Arguments434        /// 435        /// * collection_id: Id of the collection where item was created.436        /// 437        /// * item_id: Id of an item. Unique within the collection.438        ItemCreated(CollectionId, TokenId),439440        /// Collection item was burned.441        /// 442        /// # Arguments443        /// 444        /// collection_id.445        /// 446        /// item_id: Identifier of burned NFT.447        ItemDestroyed(CollectionId, TokenId),448    }449);450451decl_module! {452    pub struct Module<T: Trait> for enum Call where origin: T::Origin {453454        fn deposit_event() = default;455        type Error = Error<T>;456457        fn on_initialize(now: T::BlockNumber) -> Weight {458459            if ChainVersion::get() < 2460            {461                let value = NextCollectionID::get();462                CreatedCollectionCount::put(value);463                ChainVersion::put(2);464            }465466            0467        }468469        /// 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.470        /// 471        /// # Permissions472        /// 473        /// * Anyone.474        /// 475        /// # Arguments476        /// 477        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.478        /// 479        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.480        /// 481        /// * token_prefix: UTF-8 string with token prefix.482        /// 483        /// * mode: [CollectionMode] collection type and type dependent data.484        // returns collection ID485        #[weight = T::WeightInfo::create_collection()]486        pub fn create_collection(origin,487                                 collection_name: Vec<u16>,488                                 collection_description: Vec<u16>,489                                 token_prefix: Vec<u8>,490                                 mode: CollectionMode) -> DispatchResult {491492            // Anyone can create a collection493            let who = ensure_signed(origin)?;494495            let decimal_points = match mode {496                CollectionMode::Fungible(points) => points,497                CollectionMode::ReFungible(points) => points,498                _ => 0499            };500501            // bound Total number of collections502            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);503504            // check params505            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);506507            let mut name = collection_name.to_vec();508            name.push(0);509            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);510511            let mut description = collection_description.to_vec();512            description.push(0);513            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);514515            let mut prefix = token_prefix.to_vec();516            prefix.push(0);517            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);518519            // Generate next collection ID520            let next_id = CreatedCollectionCount::get()521                .checked_add(1)522                .ok_or(Error::<T>::NumOverflow)?;523524            // bound counter525            let total = CollectionCount::get()526                .checked_add(1)527                .ok_or(Error::<T>::NumOverflow)?;528529            CreatedCollectionCount::put(next_id);530            CollectionCount::put(total);531532            // Create new collection533            let new_collection = CollectionType {534                owner: who.clone(),535                name: name,536                mode: mode.clone(),537                mint_mode: false,538                access: AccessMode::Normal,539                description: description,540                decimal_points: decimal_points,541                token_prefix: prefix,542                offchain_schema: Vec::new(),543                sponsor: T::AccountId::default(),544                unconfirmed_sponsor: T::AccountId::default(),545                variable_on_chain_schema: Vec::new(),546                const_on_chain_schema: Vec::new(),547            };548549            // Add new collection to map550            <Collection<T>>::insert(next_id, new_collection);551552            // call event553            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));554555            Ok(())556        }557558        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.559        /// 560        /// # Permissions561        /// 562        /// * Collection Owner.563        /// 564        /// # Arguments565        /// 566        /// * collection_id: collection to destroy.567        #[weight = T::WeightInfo::destroy_collection()]568        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {569570            let sender = ensure_signed(origin)?;571            Self::check_owner_permissions(collection_id, sender)?;572573            <AddressTokens<T>>::remove_prefix(collection_id);574            <ApprovedList<T>>::remove_prefix(collection_id);575            <Balance<T>>::remove_prefix(collection_id);576            <ItemListIndex>::remove(collection_id);577            <AdminList<T>>::remove(collection_id);578            <Collection<T>>::remove(collection_id);579            <WhiteList<T>>::remove(collection_id);580581            <NftItemList<T>>::remove_prefix(collection_id);582            <FungibleItemList<T>>::remove_prefix(collection_id);583            <ReFungibleItemList<T>>::remove_prefix(collection_id);584585            <NftTransferBasket<T>>::remove_prefix(collection_id);586            <FungibleTransferBasket<T>>::remove_prefix(collection_id);587            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);588589            if CollectionCount::get() > 0590            {591                // bound couter592                let total = CollectionCount::get()593                    .checked_sub(1)594                    .ok_or(Error::<T>::NumOverflow)?;595596                CollectionCount::put(total);597            }598599            Ok(())600        }601602        /// Add an address to white list.603        /// 604        /// # Permissions605        /// 606        /// * Collection Owner607        /// * Collection Admin608        /// 609        /// # Arguments610        /// 611        /// * collection_id.612        /// 613        /// * address.614        #[weight = T::WeightInfo::add_to_white_list()]615        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{616617            let sender = ensure_signed(origin)?;618            Self::check_owner_or_admin_permissions(collection_id, sender)?;619620            let mut white_list_collection: Vec<T::AccountId>;621            if <WhiteList<T>>::contains_key(collection_id) {622                white_list_collection = <WhiteList<T>>::get(collection_id);623                if !white_list_collection.contains(&address.clone())624                {625                    white_list_collection.push(address.clone());626                }627            }628            else {629                white_list_collection = Vec::new();630                white_list_collection.push(address.clone());631            }632633            <WhiteList<T>>::insert(collection_id, white_list_collection);634            Ok(())635        }636637        /// Remove an address from white list.638        /// 639        /// # Permissions640        /// 641        /// * Collection Owner642        /// * Collection Admin643        /// 644        /// # Arguments645        /// 646        /// * collection_id.647        /// 648        /// * address.649        #[weight = T::WeightInfo::remove_from_white_list()]650        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{651652            let sender = ensure_signed(origin)?;653            Self::check_owner_or_admin_permissions(collection_id, sender)?;654655            if <WhiteList<T>>::contains_key(collection_id) {656                let mut white_list_collection = <WhiteList<T>>::get(collection_id);657                if white_list_collection.contains(&address.clone())658                {659                    white_list_collection.retain(|i| *i != address.clone());660                    <WhiteList<T>>::insert(collection_id, white_list_collection);661                }662            }663664            Ok(())665        }666667        /// Toggle between normal and white list access for the methods with access for `Anyone`.668        /// 669        /// # Permissions670        /// 671        /// * Collection Owner.672        /// 673        /// # Arguments674        /// 675        /// * collection_id.676        /// 677        /// * mode: [AccessMode]678        #[weight = T::WeightInfo::set_public_access_mode()]679        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult680        {681            let sender = ensure_signed(origin)?;682683            Self::check_owner_permissions(collection_id, sender)?;684            let mut target_collection = <Collection<T>>::get(collection_id);685            target_collection.access = mode;686            <Collection<T>>::insert(collection_id, target_collection);687688            Ok(())689        }690691        /// Allows Anyone to create tokens if:692        /// * White List is enabled, and693        /// * Address is added to white list, and694        /// * This method was called with True parameter695        /// 696        /// # Permissions697        /// * Collection Owner698        ///699        /// # Arguments700        /// 701        /// * collection_id.702        /// 703        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.704        #[weight = T::WeightInfo::set_mint_permission()]705        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult706        {707            let sender = ensure_signed(origin)?;708709            Self::check_owner_permissions(collection_id, sender)?;710            let mut target_collection = <Collection<T>>::get(collection_id);711            target_collection.mint_mode = mint_permission;712            <Collection<T>>::insert(collection_id, target_collection);713714            Ok(())715        }716717        /// Change the owner of the collection.718        /// 719        /// # Permissions720        /// 721        /// * Collection Owner.722        /// 723        /// # Arguments724        /// 725        /// * collection_id.726        /// 727        /// * new_owner.728        #[weight = T::WeightInfo::change_collection_owner()]729        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {730731            let sender = ensure_signed(origin)?;732            Self::check_owner_permissions(collection_id, sender)?;733            let mut target_collection = <Collection<T>>::get(collection_id);734            target_collection.owner = new_owner;735            <Collection<T>>::insert(collection_id, target_collection);736737            Ok(())738        }739740        /// Adds an admin of the Collection.741        /// 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. 742        /// 743        /// # Permissions744        /// 745        /// * Collection Owner.746        /// * Collection Admin.747        /// 748        /// # Arguments749        /// 750        /// * collection_id: ID of the Collection to add admin for.751        /// 752        /// * new_admin_id: Address of new admin to add.753        #[weight = T::WeightInfo::add_collection_admin()]754        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {755756            let sender = ensure_signed(origin)?;757            Self::check_owner_or_admin_permissions(collection_id, sender)?;758            let mut admin_arr: Vec<T::AccountId> = Vec::new();759760            if <AdminList<T>>::contains_key(collection_id)761            {762                admin_arr = <AdminList<T>>::get(collection_id);763                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);764            }765766            // Number of collection admins767            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);768769            admin_arr.push(new_admin_id);770            <AdminList<T>>::insert(collection_id, admin_arr);771772            Ok(())773        }774775        /// 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.776        ///777        /// # Permissions778        /// 779        /// * Collection Owner.780        /// * Collection Admin.781        /// 782        /// # Arguments783        /// 784        /// * collection_id: ID of the Collection to remove admin for.785        /// 786        /// * account_id: Address of admin to remove.787        #[weight = T::WeightInfo::remove_collection_admin()]788        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {789790            let sender = ensure_signed(origin)?;791            Self::check_owner_or_admin_permissions(collection_id, sender)?;792793            if <AdminList<T>>::contains_key(collection_id)794            {795                let mut admin_arr = <AdminList<T>>::get(collection_id);796                admin_arr.retain(|i| *i != account_id);797                <AdminList<T>>::insert(collection_id, admin_arr);798            }799800            Ok(())801        }802803        /// # Permissions804        /// 805        /// * Collection Owner806        /// 807        /// # Arguments808        /// 809        /// * collection_id.810        /// 811        /// * new_sponsor.812        #[weight = T::WeightInfo::set_collection_sponsor()]813        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {814815            let sender = ensure_signed(origin)?;816            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);817818            let mut target_collection = <Collection<T>>::get(collection_id);819            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);820821            target_collection.unconfirmed_sponsor = new_sponsor;822            <Collection<T>>::insert(collection_id, target_collection);823824            Ok(())825        }826827        /// # Permissions828        /// 829        /// * Sponsor.830        /// 831        /// # Arguments832        /// 833        /// * collection_id.834        #[weight = T::WeightInfo::confirm_sponsorship()]835        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {836837            let sender = ensure_signed(origin)?;838            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);839840            let mut target_collection = <Collection<T>>::get(collection_id);841            ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);842843            target_collection.sponsor = target_collection.unconfirmed_sponsor;844            target_collection.unconfirmed_sponsor = T::AccountId::default();845            <Collection<T>>::insert(collection_id, target_collection);846847            Ok(())848        }849850        /// Switch back to pay-per-own-transaction model.851        ///852        /// # Permissions853        ///854        /// * Collection owner.855        /// 856        /// # Arguments857        /// 858        /// * collection_id.859        #[weight = T::WeightInfo::remove_collection_sponsor()]860        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {861862            let sender = ensure_signed(origin)?;863            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);864865            let mut target_collection = <Collection<T>>::get(collection_id);866            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);867868            target_collection.sponsor = T::AccountId::default();869            <Collection<T>>::insert(collection_id, target_collection);870871            Ok(())872        }873874        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.875        /// 876        /// # Permissions877        /// 878        /// * Collection Owner.879        /// * Collection Admin.880        /// * Anyone if881        ///     * White List is enabled, and882        ///     * Address is added to white list, and883        ///     * MintPermission is enabled (see SetMintPermission method)884        /// 885        /// # Arguments886        /// 887        /// * collection_id: ID of the collection.888        /// 889        /// * owner: Address, initial owner of the NFT.890        ///891        /// * data: Token data to store on chain.892        // #[weight =893        // (130_000_000 as Weight)894        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))895        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))896        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]897898        #[weight = T::WeightInfo::create_item(data.len())]899        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {900901            let sender = ensure_signed(origin)?;902903            Self::collection_exists(collection_id)?;904905            let target_collection = <Collection<T>>::get(collection_id);906907            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;908            Self::validate_create_item_args(&target_collection, &data)?;909            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;910911            Ok(())912        }913914        /// This method creates multiple instances of NFT Collection created with CreateCollection method.915        /// 916        /// # Permissions917        /// 918        /// * Collection Owner.919        /// * Collection Admin.920        /// * Anyone if921        ///     * White List is enabled, and922        ///     * Address is added to white list, and923        ///     * MintPermission is enabled (see SetMintPermission method)924        /// 925        /// # Arguments926        /// 927        /// * collection_id: ID of the collection.928        /// 929        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].930        /// 931        /// * owner: Address, initial owner of the NFT.932        #[weight = T::WeightInfo::create_item(items_data.into_iter()933                               .map(|data| { data.len() })934                               .sum())]935        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {936937            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);938            let sender = ensure_signed(origin)?;939940            Self::collection_exists(collection_id)?;941            let target_collection = <Collection<T>>::get(collection_id);942943            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;944945            for data in &items_data {946                Self::validate_create_item_args(&target_collection, data)?;947            }948            for data in &items_data {949                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;950            }951952            Ok(())953        }954955        /// Destroys a concrete instance of NFT.956        /// 957        /// # Permissions958        /// 959        /// * Collection Owner.960        /// * Collection Admin.961        /// * Current NFT Owner.962        /// 963        /// # Arguments964        /// 965        /// * collection_id: ID of the collection.966        /// 967        /// * item_id: ID of NFT to burn.968        #[weight = T::WeightInfo::burn_item()]969        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {970971            let sender = ensure_signed(origin)?;972            Self::collection_exists(collection_id)?;973974            // Transfer permissions check975            let target_collection = <Collection<T>>::get(collection_id);976            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||977                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),978                Error::<T>::NoPermission);979980            if target_collection.access == AccessMode::WhiteList {981                Self::check_white_list(collection_id, &sender)?;982            }983984            match target_collection.mode985            {986                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,987                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,988                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,989                _ => ()990            };991992            // call event993            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));994995            Ok(())996        }997998        /// Change ownership of the token.999        /// 1000        /// # Permissions1001        /// 1002        /// * Collection Owner1003        /// * Collection Admin1004        /// * Current NFT owner1005        ///1006        /// # Arguments1007        /// 1008        /// * recipient: Address of token recipient.1009        /// 1010        /// * collection_id.1011        /// 1012        /// * item_id: ID of the item1013        ///     * Non-Fungible Mode: Required.1014        ///     * Fungible Mode: Ignored.1015        ///     * Re-Fungible Mode: Required.1016        /// 1017        /// * value: Amount to transfer.1018        ///     * Non-Fungible Mode: Ignored1019        ///     * Fungible Mode: Must specify transferred amount1020        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1021        #[weight = T::WeightInfo::transfer()]1022        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u64) -> DispatchResult {10231024            let sender = ensure_signed(origin)?;10251026            // Transfer permissions check1027            let target_collection = <Collection<T>>::get(collection_id);1028            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1029                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1030                Error::<T>::NoPermission);10311032            if target_collection.access == AccessMode::WhiteList {1033                Self::check_white_list(collection_id, &sender)?;1034                Self::check_white_list(collection_id, &recipient)?;1035            }10361037            match target_collection.mode1038            {1039                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1040                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1041                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1042                _ => ()1043            };10441045            Ok(())1046        }10471048        /// Set, change, or remove approved address to transfer the ownership of the NFT.1049        /// 1050        /// # Permissions1051        /// 1052        /// * Collection Owner1053        /// * Collection Admin1054        /// * Current NFT owner1055        /// 1056        /// # Arguments1057        /// 1058        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1059        /// 1060        /// * collection_id.1061        /// 1062        /// * item_id: ID of the item.1063        #[weight = T::WeightInfo::approve()]1064        pub fn approve(origin, approved: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {10651066            let sender = ensure_signed(origin)?;10671068            // Transfer permissions check1069            let target_collection = <Collection<T>>::get(collection_id);1070            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1071                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1072                Error::<T>::NoPermission);10731074            if target_collection.access == AccessMode::WhiteList {1075                Self::check_white_list(collection_id, &sender)?;1076                Self::check_white_list(collection_id, &approved)?;1077            }10781079            // amount param stub1080            let amount = 100000000;10811082            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1083            if list_exists {10841085                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1086                let item_contains = list.iter().any(|i| i.approved == approved);10871088                if !item_contains {1089                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1090                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1091                }1092            } else {10931094                let mut list = Vec::new();1095                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1096                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1097            }10981099            Ok(())1100        }1101        1102        /// 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.1103        /// 1104        /// # Permissions1105        /// * Collection Owner1106        /// * Collection Admin1107        /// * Current NFT owner1108        /// * Address approved by current NFT owner1109        /// 1110        /// # Arguments1111        /// 1112        /// * from: Address that owns token.1113        /// 1114        /// * recipient: Address of token recipient.1115        /// 1116        /// * collection_id.1117        /// 1118        /// * item_id: ID of the item.1119        /// 1120        /// * value: Amount to transfer.1121        #[weight = T::WeightInfo::transfer_from()]1122        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u64 ) -> DispatchResult {11231124            let sender = ensure_signed(origin)?;1125            let mut appoved_transfer = false;11261127            // Check approve1128            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1129                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1130                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1131                if opt_item.is_some()1132                {1133                    appoved_transfer = true;1134                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1135                }1136            }11371138            // Transfer permissions check1139            let target_collection = <Collection<T>>::get(collection_id);1140                ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1141                Error::<T>::NoPermission);11421143            if target_collection.access == AccessMode::WhiteList {1144                Self::check_white_list(collection_id, &sender)?;1145                Self::check_white_list(collection_id, &recipient)?;1146            }11471148            // remove approve1149            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1150                .into_iter().filter(|i| i.approved != sender.clone()).collect();1151            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);115211531154            match target_collection.mode1155            {1156                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1157                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1158                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1159                _ => ()1160            };11611162            Ok(())1163        }11641165        ///1166        #[weight = 0]1167        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11681169            // let no_perm_mes = "You do not have permissions to modify this collection";1170            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1171            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1172            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11731174            // // on_nft_received  call11751176            // Self::transfer(origin, collection_id, item_id, new_owner)?;11771178            Ok(())1179        }11801181        /// Set off-chain data schema.1182        /// 1183        /// # Permissions1184        /// 1185        /// * Collection Owner1186        /// * Collection Admin1187        /// 1188        /// # Arguments1189        /// 1190        /// * collection_id.1191        /// 1192        /// * schema: String representing the offchain data schema.1193        #[weight = T::WeightInfo::set_variable_meta_data()]1194        pub fn set_variable_meta_data (1195            origin,1196            collection_id: CollectionId,1197            item_id: TokenId,1198            data: Vec<u8>1199        ) -> DispatchResult {1200            let sender = ensure_signed(origin)?;1201            1202            Self::collection_exists(collection_id)?;1203            1204            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12051206            // Modify permissions check1207            let target_collection = <Collection<T>>::get(collection_id);1208            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1209                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1210                Error::<T>::NoPermission);12111212            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12131214            match target_collection.mode1215            {1216                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1217                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1218                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1219                _ => fail!(Error::<T>::UnexpectedCollectionType)1220            };12211222            Ok(())1223        }1224        12251226        /// Set off-chain data schema.1227        /// 1228        /// # Permissions1229        /// 1230        /// * Collection Owner1231        /// * Collection Admin1232        /// 1233        /// # Arguments1234        /// 1235        /// * collection_id.1236        /// 1237        /// * schema: String representing the offchain data schema.1238        #[weight = T::WeightInfo::set_offchain_schema()]1239        pub fn set_offchain_schema(1240            origin,1241            collection_id: CollectionId,1242            schema: Vec<u8>1243        ) -> DispatchResult {1244            let sender = ensure_signed(origin)?;1245            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12461247            let mut target_collection = <Collection<T>>::get(collection_id);1248            target_collection.offchain_schema = schema;1249            <Collection<T>>::insert(collection_id, target_collection);12501251            Ok(())1252        }12531254        /// Set const on-chain data schema.1255        /// 1256        /// # Permissions1257        /// 1258        /// * Collection Owner1259        /// * Collection Admin1260        /// 1261        /// # Arguments1262        /// 1263        /// * collection_id.1264        /// 1265        /// * schema: String representing the const on-chain data schema.1266        #[weight = T::WeightInfo::set_const_on_chain_schema()]1267        pub fn set_const_on_chain_schema (1268            origin,1269            collection_id: CollectionId,1270            schema: Vec<u8>1271        ) -> DispatchResult {1272            let sender = ensure_signed(origin)?;1273            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12741275            let mut target_collection = <Collection<T>>::get(collection_id);1276            target_collection.const_on_chain_schema = schema;1277            <Collection<T>>::insert(collection_id, target_collection);12781279            Ok(())1280        }12811282        /// Set variable on-chain data schema.1283        /// 1284        /// # Permissions1285        /// 1286        /// * Collection Owner1287        /// * Collection Admin1288        /// 1289        /// # Arguments1290        /// 1291        /// * collection_id.1292        /// 1293        /// * schema: String representing the variable on-chain data schema.1294        #[weight = T::WeightInfo::set_const_on_chain_schema()]1295        pub fn set_variable_on_chain_schema (1296            origin,1297            collection_id: CollectionId,1298            schema: Vec<u8>1299        ) -> DispatchResult {1300            let sender = ensure_signed(origin)?;1301            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13021303            let mut target_collection = <Collection<T>>::get(collection_id);1304            target_collection.variable_on_chain_schema = schema;1305            <Collection<T>>::insert(collection_id, target_collection);13061307            Ok(())1308        }13091310        // Sudo permissions function1311        #[weight = 0]1312        pub fn set_chain_limits(1313            origin,1314            limits: ChainLimits1315        ) -> DispatchResult {1316            ensure_root(origin)?;1317            <ChainLimit>::put(limits);1318            Ok(())1319        }13201321        /// Enable smart contract self-sponsoring.1322        /// 1323        /// # Permissions1324        /// 1325        /// * Contract Owner1326        /// 1327        /// # Arguments1328        /// 1329        /// * contract address1330        /// * enable flag1331        /// 1332        #[weight = T::WeightInfo::enable_contract_sponsoring()]1333        pub fn enable_contract_sponsoring(1334            origin,1335            contract_address: T::AccountId,1336            enable: bool1337        ) -> DispatchResult {13381339            let sender = ensure_signed(origin)?;13401341            #[cfg(feature = "runtime-benchmarks")]1342            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13431344            let mut is_owner = false;1345            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1346                let owner = <ContractOwner<T>>::get(&contract_address);1347                is_owner = sender == owner;1348            }1349            ensure!(is_owner, Error::<T>::NoPermission);13501351            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1352            Ok(())1353        }13541355        /// Set the rate limit for contract sponsoring to specified number of blocks.1356        /// 1357        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1358        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1359        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1360        /// from contract endowment if there are at least B blocks between such transactions. 1361        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1362        /// 1363        /// # Permissions1364        /// 1365        /// * Contract Owner1366        /// 1367        /// # Arguments1368        /// 1369        /// -`contract_address`: Address of the contract to sponsor1370        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1371        /// 1372        #[weight = 0]1373        pub fn set_contract_sponsoring_rate_limit(1374            origin,1375            contract_address: T::AccountId,1376            rate_limit: T::BlockNumber1377        ) -> DispatchResult {1378            let sender = ensure_signed(origin)?;1379            let mut is_owner = false;1380            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1381                let owner = <ContractOwner<T>>::get(&contract_address);1382                is_owner = sender == owner;1383            }1384            ensure!(is_owner, Error::<T>::NoPermission);13851386            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1387            Ok(())1388        }13891390        // #[cfg(feature = "runtime-benchmarks")]1391        // #[weight = 0]1392        // pub fn add_contract_sponsoring_debug(1393        //     origin,1394        //     contract_address: T::AccountId, 1395        //     owner: T::AccountId) -> DispatchResult {1396        //     let sender = ensure_signed(origin)?;1397        //     <ContractOwner<T>>::insert(contract_address.clone(), owner);1398        //     Ok(())1399        // }1400    1401    }1402}14031404impl<T: Trait> Module<T> {14051406    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {14071408        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1409            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1410            Self::check_white_list(collection_id, owner)?;1411            Self::check_white_list(collection_id, sender)?;1412        }14131414        Ok(())1415    }14161417    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1418        match target_collection.mode1419        {1420            CollectionMode::NFT => {1421                if let CreateItemData::NFT(data) = data {1422                    // check sizes1423                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1424                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1425                } else {1426                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1427                }1428            },1429            CollectionMode::Fungible(_) => {1430                if let CreateItemData::Fungible(_) = data {1431                } else {1432                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1433                }1434            },1435            CollectionMode::ReFungible(_) => {1436                if let CreateItemData::ReFungible(data) = data {14371438                    // check sizes1439                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1440                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1441                } else {1442                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1443                }1444            },1445            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1446        };14471448        Ok(())1449    }14501451    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1452        match data1453        {1454            CreateItemData::NFT(data) => {1455                let item = NftItemType {1456                    collection: collection_id,1457                    owner,1458                    const_data: data.const_data,1459                    variable_data: data.variable_data1460                };14611462                Self::add_nft_item(item)?;1463            },1464            CreateItemData::Fungible(_) => {1465                let item = FungibleItemType {1466                    collection: collection_id,1467                    owner,1468                    value: (10 as u128).pow(collection.decimal_points as u32)1469                };14701471                Self::add_fungible_item(item)?;1472            },1473            CreateItemData::ReFungible(data) => {1474                let mut owner_list = Vec::new();1475                let value = (10 as u128).pow(collection.decimal_points as u32);1476                owner_list.push(Ownership {owner: owner.clone(), fraction: value});14771478                let item = ReFungibleItemType {1479                    collection: collection_id,1480                    owner: owner_list,1481                    const_data: data.const_data,1482                    variable_data: data.variable_data1483                };14841485                Self::add_refungible_item(item)?;1486            }1487        };148814891490        // call event1491        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));14921493        Ok(())1494    }14951496    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1497        let current_index = <ItemListIndex>::get(item.collection)1498            .checked_add(1)1499            .ok_or(Error::<T>::NumOverflow)?;1500        let itemcopy = item.clone();1501        let owner = item.owner.clone();1502        let value = item.value as u64;15031504        Self::add_token_index(item.collection, current_index, owner.clone())?;15051506        <ItemListIndex>::insert(item.collection, current_index);1507        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15081509        // Add current block1510        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1511        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1512        1513        // Update balance1514        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1515            .checked_add(value)1516            .ok_or(Error::<T>::NumOverflow)?;1517        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15181519        Ok(())1520    }15211522    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1523        let current_index = <ItemListIndex>::get(item.collection)1524            .checked_add(1)1525            .ok_or(Error::<T>::NumOverflow)?;1526        let itemcopy = item.clone();15271528        let value = item.owner.first().unwrap().fraction as u64;1529        let owner = item.owner.first().unwrap().owner.clone();15301531        Self::add_token_index(item.collection, current_index, owner.clone())?;15321533        <ItemListIndex>::insert(item.collection, current_index);1534        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15351536        // Add current block1537        let block_number: T::BlockNumber = 0.into();1538        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);15391540        // Update balance1541        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1542            .checked_add(value)1543            .ok_or(Error::<T>::NumOverflow)?;1544        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15451546        Ok(())1547    }15481549    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1550        let current_index = <ItemListIndex>::get(item.collection)1551            .checked_add(1)1552            .ok_or(Error::<T>::NumOverflow)?;15531554        let item_owner = item.owner.clone();1555        let collection_id = item.collection.clone();1556        Self::add_token_index(collection_id, current_index, item.owner.clone())?;15571558        <ItemListIndex>::insert(collection_id, current_index);1559        <NftItemList<T>>::insert(collection_id, current_index, item);15601561        // Add current block1562        let block_number: T::BlockNumber = 0.into();1563        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);15641565        // Update balance1566        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1567            .checked_add(1)1568            .ok_or(Error::<T>::NumOverflow)?;1569        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);15701571        Ok(())1572    }15731574    fn burn_refungible_item(1575        collection_id: CollectionId,1576        item_id: TokenId,1577        owner: T::AccountId,1578    ) -> DispatchResult {1579        ensure!(1580            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1581            Error::<T>::TokenNotFound1582        );1583        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1584        let item = collection1585            .owner1586            .iter()1587            .filter(|&i| i.owner == owner)1588            .next()1589            .unwrap();1590        Self::remove_token_index(collection_id, item_id, owner.clone())?;15911592        // remove approve list1593        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));15941595        // update balance1596        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1597            .checked_sub(item.fraction as u64)1598            .ok_or(Error::<T>::NumOverflow)?;1599        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);16001601        <ReFungibleItemList<T>>::remove(collection_id, item_id);16021603        Ok(())1604    }16051606    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1607        ensure!(1608            <NftItemList<T>>::contains_key(collection_id, item_id),1609            Error::<T>::TokenNotFound1610        );1611        let item = <NftItemList<T>>::get(collection_id, item_id);1612        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16131614        // remove approve list1615        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16161617        // update balance1618        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1619            .checked_sub(1)1620            .ok_or(Error::<T>::NumOverflow)?;1621        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1622        <NftItemList<T>>::remove(collection_id, item_id);16231624        Ok(())1625    }16261627    fn burn_fungible_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1628        ensure!(1629            <FungibleItemList<T>>::contains_key(collection_id, item_id),1630            Error::<T>::TokenNotFound1631        );1632        let item = <FungibleItemList<T>>::get(collection_id, item_id);1633        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16341635        // remove approve list1636        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16371638        // update balance1639        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1640            .checked_sub(item.value as u64)1641            .ok_or(Error::<T>::NumOverflow)?;1642        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);16431644        <FungibleItemList<T>>::remove(collection_id, item_id);16451646        Ok(())1647    }16481649    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1650        ensure!(1651            <Collection<T>>::contains_key(collection_id),1652            Error::<T>::CollectionNotFound1653        );1654        Ok(())1655    }16561657    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1658        Self::collection_exists(collection_id)?;16591660        let target_collection = <Collection<T>>::get(collection_id);1661        ensure!(1662            subject == target_collection.owner,1663            Error::<T>::NoPermission1664        );16651666        Ok(())1667    }16681669    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1670        let target_collection = <Collection<T>>::get(collection_id);1671        let mut result: bool = subject == target_collection.owner;1672        let exists = <AdminList<T>>::contains_key(collection_id);16731674        if !result & exists {1675            if <AdminList<T>>::get(collection_id).contains(&subject) {1676                result = true1677            }1678        }16791680        result1681    }16821683    fn check_owner_or_admin_permissions(1684        collection_id: CollectionId,1685        subject: T::AccountId,1686    ) -> DispatchResult {1687        Self::collection_exists(collection_id)?;1688        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());16891690        ensure!(1691            result,1692            Error::<T>::NoPermission1693        );1694        Ok(())1695    }16961697    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1698        let target_collection = <Collection<T>>::get(collection_id);16991700        match target_collection.mode {1701            CollectionMode::NFT => {1702                <NftItemList<T>>::get(collection_id, item_id).owner == subject1703            }1704            CollectionMode::Fungible(_) => {1705                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1706            }1707            CollectionMode::ReFungible(_) => {1708                <ReFungibleItemList<T>>::get(collection_id, item_id)1709                    .owner1710                    .iter()1711                    .any(|i| i.owner == subject)1712            }1713            CollectionMode::Invalid => false,1714        }1715    }17161717    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1718        let mes = Error::<T>::AddresNotInWhiteList;1719        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1720        let wl = <WhiteList<T>>::get(collection_id);1721        ensure!(wl.contains(address), mes);17221723        Ok(())1724    }17251726    fn transfer_fungible(1727        collection_id: CollectionId,1728        item_id: TokenId,1729        value: u64,1730        owner: T::AccountId,1731        new_owner: T::AccountId,1732    ) -> DispatchResult {1733        ensure!(1734            <FungibleItemList<T>>::contains_key(collection_id, item_id),1735            Error::<T>::TokenNotFound1736        );17371738        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1739        let amount = full_item.value;17401741        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);17421743        // update balance1744        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1745            .checked_sub(value)1746            .ok_or(Error::<T>::NumOverflow)?;1747        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);17481749        let mut new_owner_account_id = 0;1750        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1751        if new_owner_items.len() > 0 {1752            new_owner_account_id = new_owner_items[0];1753        }17541755        let val64 = value.into();17561757        // transfer1758        if amount == val64 && new_owner_account_id == 0 {1759            // change owner1760            // new owner do not have account1761            let mut new_full_item = full_item.clone();1762            new_full_item.owner = new_owner.clone();1763            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17641765            // update balance1766            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1767                .checked_add(value)1768                .ok_or(Error::<T>::NumOverflow)?;1769            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17701771            // update index collection1772            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1773        } else {1774            let mut new_full_item = full_item.clone();1775            new_full_item.value -= val64;17761777            // separate amount1778            if new_owner_account_id > 0 {1779                // new owner has account1780                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1781                item.value += val64;17821783                // update balance1784                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1785                    .checked_add(value)1786                    .ok_or(Error::<T>::NumOverflow)?;1787                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17881789                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1790            } else {1791                // new owner do not have account1792                let item = FungibleItemType {1793                    collection: collection_id,1794                    owner: new_owner.clone(),1795                    value: val64,1796                };17971798                Self::add_fungible_item(item)?;1799            }18001801            if amount == val64 {1802                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;18031804                // remove approve list1805                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1806                <FungibleItemList<T>>::remove(collection_id, item_id);1807            }18081809            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1810        }18111812        Ok(())1813    }18141815    fn transfer_refungible(1816        collection_id: CollectionId,1817        item_id: TokenId,1818        value: u64,1819        owner: T::AccountId,1820        new_owner: T::AccountId,1821    ) -> DispatchResult {1822        ensure!(1823            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1824            Error::<T>::TokenNotFound1825        );18261827        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1828        let item = full_item1829            .owner1830            .iter()1831            .filter(|i| i.owner == owner)1832            .next()1833            .ok_or(Error::<T>::NumOverflow)?;1834        let amount = item.fraction;18351836        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);18371838        // update balance1839        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1840            .checked_sub(value)1841            .ok_or(Error::<T>::NumOverflow)?;1842        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18431844        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1845            .checked_add(value)1846            .ok_or(Error::<T>::NumOverflow)?;1847        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18481849        let old_owner = item.owner.clone();1850        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1851        let val64 = value.into();18521853        // transfer1854        if amount == val64 && !new_owner_has_account {1855            // change owner1856            // new owner do not have account1857            let mut new_full_item = full_item.clone();1858            new_full_item1859                .owner1860                .iter_mut()1861                .find(|i| i.owner == owner)1862                .unwrap()1863                .owner = new_owner.clone();1864            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18651866            // update index collection1867            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1868        } else {1869            let mut new_full_item = full_item.clone();1870            new_full_item1871                .owner1872                .iter_mut()1873                .find(|i| i.owner == owner)1874                .unwrap()1875                .fraction -= val64;18761877            // separate amount1878            if new_owner_has_account {1879                // new owner has account1880                new_full_item1881                    .owner1882                    .iter_mut()1883                    .find(|i| i.owner == new_owner)1884                    .unwrap()1885                    .fraction += val64;1886            } else {1887                // new owner do not have account1888                new_full_item.owner.push(Ownership {1889                    owner: new_owner.clone(),1890                    fraction: val64,1891                });1892                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1893            }18941895            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1896        }18971898        Ok(())1899    }19001901    fn transfer_nft(1902        collection_id: CollectionId,1903        item_id: TokenId,1904        sender: T::AccountId,1905        new_owner: T::AccountId,1906    ) -> DispatchResult {1907        ensure!(1908            <NftItemList<T>>::contains_key(collection_id, item_id),1909            Error::<T>::TokenNotFound1910        );19111912        let mut item = <NftItemList<T>>::get(collection_id, item_id);19131914        ensure!(1915            sender == item.owner,1916            Error::<T>::MustBeTokenOwner1917        );19181919        // update balance1920        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1921            .checked_sub(1)1922            .ok_or(Error::<T>::NumOverflow)?;1923        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19241925        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1926            .checked_add(1)1927            .ok_or(Error::<T>::NumOverflow)?;1928        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19291930        // change owner1931        let old_owner = item.owner.clone();1932        item.owner = new_owner.clone();1933        <NftItemList<T>>::insert(collection_id, item_id, item);19341935        // update index collection1936        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;19371938        // reset approved list1939        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1940        Ok(())1941    }1942    1943    fn item_exists(1944        collection_id: CollectionId,1945        item_id: TokenId,1946        mode: &CollectionMode1947    ) -> DispatchResult {1948        match mode {1949            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1950            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1951            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1952            _ => ()1953        };1954        1955        Ok(())1956    }19571958    fn set_re_fungible_variable_data(1959        collection_id: CollectionId,1960        item_id: TokenId,1961        data: Vec<u8>1962    ) -> DispatchResult {1963        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);19641965        item.variable_data = data;19661967        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);19681969        Ok(())1970    }19711972    fn set_nft_variable_data(1973        collection_id: CollectionId,1974        item_id: TokenId,1975        data: Vec<u8>1976    ) -> DispatchResult {1977        let mut item = <NftItemList<T>>::get(collection_id, item_id);1978        1979        item.variable_data = data;19801981        <NftItemList<T>>::insert(collection_id, item_id, item);1982        1983        Ok(())1984    }19851986    fn init_collection(item: &CollectionType<T::AccountId>) {1987        // check params1988        assert!(1989            item.decimal_points <= MAX_DECIMAL_POINTS,1990            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"1991        );1992        assert!(1993            item.name.len() <= 64,1994            "Collection name can not be longer than 63 char"1995        );1996        assert!(1997            item.name.len() <= 256,1998            "Collection description can not be longer than 255 char"1999        );2000        assert!(2001            item.token_prefix.len() <= 16,2002            "Token prefix can not be longer than 15 char"2003        );20042005        // Generate next collection ID2006        let next_id = CreatedCollectionCount::get()2007            .checked_add(1)2008            .unwrap();20092010        CreatedCollectionCount::put(next_id);2011    }20122013    fn init_nft_token(item: &NftItemType<T::AccountId>) {2014        let current_index = <ItemListIndex>::get(item.collection)2015            .checked_add(1)2016            .unwrap();20172018        let item_owner = item.owner.clone();2019        let collection_id = item.collection.clone();2020        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();20212022        <ItemListIndex>::insert(collection_id, current_index);20232024        // Update balance2025        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2026            .checked_add(1)2027            .unwrap();2028        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2029    }20302031    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2032        let current_index = <ItemListIndex>::get(item.collection)2033            .checked_add(1)2034            .unwrap();2035        let owner = item.owner.clone();2036        let value = item.value as u64;20372038        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20392040        <ItemListIndex>::insert(item.collection, current_index);20412042        // Update balance2043        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2044            .checked_add(value)2045            .unwrap();2046        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2047    }20482049    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2050        let current_index = <ItemListIndex>::get(item.collection)2051            .checked_add(1)2052            .unwrap();20532054        let value = item.owner.first().unwrap().fraction as u64;2055        let owner = item.owner.first().unwrap().owner.clone();20562057        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20582059        <ItemListIndex>::insert(item.collection, current_index);20602061        // Update balance2062        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2063            .checked_add(value)2064            .unwrap();2065        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2066    }20672068    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {20692070        // add to account limit2071        if <AccountItemCount<T>>::contains_key(owner.clone()) {20722073            // bound Owned tokens by a single address2074            let count = <AccountItemCount<T>>::get(owner.clone());2075            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);20762077            <AccountItemCount<T>>::insert(owner.clone(), count2078                .checked_add(1)2079                .ok_or(Error::<T>::NumOverflow)?);2080        }2081        else {2082            <AccountItemCount<T>>::insert(owner.clone(), 1);2083        }20842085        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2086        if list_exists {2087            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2088            let item_contains = list.contains(&item_index.clone());20892090            if !item_contains {2091                list.push(item_index.clone());2092            }20932094            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2095        } else {2096            let mut itm = Vec::new();2097            itm.push(item_index.clone());2098            <AddressTokens<T>>::insert(collection_id, owner, itm);2099            2100        }21012102        Ok(())2103    }21042105    fn remove_token_index(2106        collection_id: CollectionId,2107        item_index: TokenId,2108        owner: T::AccountId,2109    ) -> DispatchResult {21102111        // update counter2112        <AccountItemCount<T>>::insert(owner.clone(), 2113            <AccountItemCount<T>>::get(owner.clone())2114            .checked_sub(1)2115            .ok_or(Error::<T>::NumOverflow)?);211621172118        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2119        if list_exists {2120            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2121            let item_contains = list.contains(&item_index.clone());21222123            if item_contains {2124                list.retain(|&item| item != item_index);2125                <AddressTokens<T>>::insert(collection_id, owner, list);2126            }2127        }21282129        Ok(())2130    }21312132    fn move_token_index(2133        collection_id: CollectionId,2134        item_index: TokenId,2135        old_owner: T::AccountId,2136        new_owner: T::AccountId,2137    ) -> DispatchResult {2138        Self::remove_token_index(collection_id, item_index, old_owner)?;2139        Self::add_token_index(collection_id, item_index, new_owner)?;21402141        Ok(())2142    }2143}21442145////////////////////////////////////////////////////////////////////////////////////////////////////2146// Economic models2147// #region21482149/// Fee multiplier.2150pub type Multiplier = FixedU128;21512152type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2153    <T as system::Trait>::AccountId,2154>>::Balance;2155type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2156    <T as system::Trait>::AccountId,2157>>::NegativeImbalance;21582159/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2160/// in the queue.2161#[derive(Encode, Decode, Clone, Eq, PartialEq)]2162pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2163    #[codec(compact)] BalanceOf<T>2164);21652166impl<T: Trait + Send + Sync> sp_std::fmt::Debug2167    for ChargeTransactionPayment<T>2168{2169    #[cfg(feature = "std")]2170    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2171        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2172    }2173    #[cfg(not(feature = "std"))]2174    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2175        Ok(())2176    }2177}21782179impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2180where2181    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2182    BalanceOf<T>: Send + Sync + FixedPointOperand,2183{2184    /// utility constructor. Used only in client/factory code.2185    pub fn from(fee: BalanceOf<T>) -> Self {2186        Self(fee)2187    }21882189    pub fn traditional_fee(2190        len: usize,2191        info: &DispatchInfoOf<T::Call>,2192        tip: BalanceOf<T>,2193    ) -> BalanceOf<T>2194    where2195        T::Call: Dispatchable<Info = DispatchInfo>,2196    {2197        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2198    }21992200    fn withdraw_fee(2201        &self,2202        who: &T::AccountId,2203        call: &T::Call,2204        info: &DispatchInfoOf<T::Call>,2205        len: usize,2206    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2207        let tip = self.0;22082209        // Set fee based on call type. Creating collection costs 1 Unique.2210        // All other transactions have traditional fees so far2211        // let fee = match call.is_sub_type() {2212        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2213        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2214        //                                                 // _ => <BalanceOf<T>>::from(100)2215        // };2216        let fee = Self::traditional_fee(len, info, tip);22172218        // Determine who is paying transaction fee based on ecnomic model2219        // Parse call to extract collection ID and access collection sponsor2220        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2221            Some(Call::create_item(collection_id, _properties, _owner)) => {2222                <Collection<T>>::get(collection_id).sponsor2223            }2224            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2225                let _collection_mode = <Collection<T>>::get(collection_id).mode;22262227                // sponsor timeout2228                let sponsor_transfer = match _collection_mode {2229                    CollectionMode::NFT => {2230                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2231                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2232                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2233                        if block_number >= limit_time {2234                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2235                            true2236                        }2237                        else {2238                            false2239                        }2240                    }2241                    CollectionMode::Fungible(_) => {2242                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2243                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2244                        if basket.iter().any(|i| i.address == _new_owner.clone())2245                        {2246                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2247                            let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2248                            if block_number >= limit_time {2249                                basket.retain(|x| x.address == item.address);2250                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2251                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2252                                true2253                            }2254                            else {2255                                false2256                            }2257                        }2258                        else {2259                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2260                            true2261                        }2262                    }2263                    CollectionMode::ReFungible(_) => {2264                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2265                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2266                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2267                        if block_number >= limit_time {2268                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2269                            true2270                        } else {2271                            false2272                        }2273                    }2274                    _ => {2275                        false2276                    },2277                };22782279                if !sponsor_transfer {2280                    T::AccountId::default()2281                } else {2282                    <Collection<T>>::get(collection_id).sponsor2283                }2284            }22852286            _ => T::AccountId::default(),2287        };22882289        // Sponsor smart contracts2290        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {22912292            // On instantiation: set the contract owner2293            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {22942295                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2296                    code_hash,2297                    &data,2298                    &who,2299                );2300                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());23012302                T::AccountId::default()2303            },23042305            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2306            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {23072308                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());23092310                let mut sponsor_transfer = false;2311                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2312                    let last_tx_block = <ContractSponsorBasket<T>>::get(&called_contract);2313                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2314                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2315                    let limit_time = last_tx_block + rate_limit;23162317                    if block_number >= limit_time {2318                        <ContractSponsorBasket<T>>::insert(called_contract.clone(), block_number);2319                        sponsor_transfer = true;2320                    }2321                } else {2322                    sponsor_transfer = false;2323                }2324               2325                2326                let mut sp = T::AccountId::default();2327                if sponsor_transfer {2328                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2329                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2330                            sp = called_contract;2331                        }2332                    }2333                }23342335                sp2336            },23372338            _ => sponsor,2339        };23402341        let mut who_pays_fee: T::AccountId = sponsor.clone();2342        if sponsor == T::AccountId::default() {2343            who_pays_fee = who.clone();2344        }23452346        // Only mess with balances if fee is not zero.2347        if fee.is_zero() {2348            return Ok((fee, None));2349        }23502351        match <T as transaction_payment::Trait>::Currency::withdraw(2352            &who_pays_fee,2353            fee,2354            if tip.is_zero() {2355                WithdrawReason::TransactionPayment.into()2356            } else {2357                WithdrawReason::TransactionPayment | WithdrawReason::Tip2358            },2359            ExistenceRequirement::KeepAlive,2360        ) {2361            Ok(imbalance) => Ok((fee, Some(imbalance))),2362            Err(_) => Err(InvalidTransaction::Payment.into()),2363        }2364    }2365}236623672368impl<T: Trait + Send + Sync> SignedExtension2369    for ChargeTransactionPayment<T>2370where2371    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2372    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2373{2374    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2375    type AccountId = T::AccountId;2376    type Call = T::Call;2377    type AdditionalSigned = ();2378    type Pre = (2379        BalanceOf<T>,2380        Self::AccountId,2381        Option<NegativeImbalanceOf<T>>,2382        BalanceOf<T>,2383    );2384    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2385        Ok(())2386    }23872388    fn validate(2389        &self,2390        _who: &Self::AccountId,2391        _call: &Self::Call,2392        _info: &DispatchInfoOf<Self::Call>,2393        _len: usize,2394    ) -> TransactionValidity {2395        Ok(ValidTransaction::default())2396    }23972398    fn pre_dispatch(2399        self,2400        who: &Self::AccountId,2401        call: &Self::Call,2402        info: &DispatchInfoOf<Self::Call>,2403        len: usize,2404    ) -> Result<Self::Pre, TransactionValidityError> {2405        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2406        Ok((self.0, who.clone(), imbalance, fee))2407    }24082409    fn post_dispatch(2410        pre: Self::Pre,2411        info: &DispatchInfoOf<Self::Call>,2412        post_info: &PostDispatchInfoOf<Self::Call>,2413        len: usize,2414        _result: &DispatchResult,2415    ) -> Result<(), TransactionValidityError> {2416        let (tip, who, imbalance, fee) = pre;2417        if let Some(payed) = imbalance {2418            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2419                len as u32, info, post_info, tip,2420            );2421            let refund = fee.saturating_sub(actual_fee);2422            let actual_payment =2423                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2424                    &who, refund,2425                ) {2426                    Ok(refund_imbalance) => {2427                        // The refund cannot be larger than the up front payed max weight.2428                        // `PostDispatchInfo::calc_unspent` guards against such a case.2429                        match payed.offset(refund_imbalance) {2430                            Ok(actual_payment) => actual_payment,2431                            Err(_) => return Err(InvalidTransaction::Payment.into()),2432                        }2433                    }2434                    // We do not recreate the account using the refund. The up front payment2435                    // is gone in that case.2436                    Err(_) => payed,2437                };2438            let imbalances = actual_payment.split(tip);2439            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2440                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2441            );2442        }2443        Ok(())2444    }2445}24462447// #endregion
modifiedpallets/nft/src/tests.rsdiffbeforeafterboth
--- a/pallets/nft/src/tests.rs
+++ b/pallets/nft/src/tests.rs
@@ -3,7 +3,7 @@
 use crate::mock::*;
 use crate::{AccessMode, ApprovePermissions, CollectionMode,
     Ownership, ChainLimits, CreateItemData, CreateNftData, CreateFungibleData, CreateReFungibleData,
-    CollectionId, TokenId}; //Err
+    CollectionId, TokenId, MAX_DECIMAL_POINTS}; //Err
 use frame_support::{assert_noop, assert_ok};
 use frame_system::{ RawOrigin };
 
@@ -78,6 +78,46 @@
 // Use cases tests region
 // #region
 #[test]
+fn create_fungible_collection_fails_with_large_decimal_numbers() {
+    new_test_ext().execute_with(|| {
+        default_limits();
+
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+
+        let origin1 = Origin::signed(1);
+        assert_noop!(TemplateModule::create_collection(
+            origin1,
+            col_name1,
+            col_desc1,
+            token_prefix1,
+            CollectionMode::Fungible(MAX_DECIMAL_POINTS + 1)
+        ), Error::<Test>::CollectionDecimalPointLimitExceeded);
+    });    
+}
+
+#[test]
+fn create_re_fungible_collection_fails_with_large_decimal_numbers() {
+    new_test_ext().execute_with(|| {
+        default_limits();
+
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+
+        let origin1 = Origin::signed(1);
+        assert_noop!(TemplateModule::create_collection(
+            origin1,
+            col_name1,
+            col_desc1,
+            token_prefix1,
+            CollectionMode::ReFungible(MAX_DECIMAL_POINTS + 1)
+        ), Error::<Test>::CollectionDecimalPointLimitExceeded);
+    });
+}
+
+#[test]
 fn create_nft_item() {
     new_test_ext().execute_with(|| {
         default_limits();