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

difftreelog

Refcator token types - remove collection ID

Greg Zaitsev2020-12-24parent: #427c943.patch.diff
in: master

2 files changed

modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
before · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, WithdrawReason,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial,29    },30    IsSubType, StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use pallet_contracts::ContractAddressFor;45use sp_runtime::traits::StaticLookup;4647#[cfg(test)]48mod mock;4950#[cfg(test)]51mod tests;5253mod default_weights;5455pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;56pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;57pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;5859// Structs60// #region6162pub type CollectionId = u32;63pub type TokenId = u32;64pub type DecimalPoints = u8;6566#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]67#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]68pub enum CollectionMode {69    Invalid,70    NFT,71    // decimal points72    Fungible(DecimalPoints),73    // decimal points74    ReFungible(DecimalPoints),75}7677impl Into<u8> for CollectionMode {78    fn into(self) -> u8 {79        match self {80            CollectionMode::Invalid => 0,81            CollectionMode::NFT => 1,82            CollectionMode::Fungible(_) => 2,83            CollectionMode::ReFungible(_) => 3,84        }85    }86}8788#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]89#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]90pub enum AccessMode {91    Normal,92    WhiteList,93}94impl Default for AccessMode {95    fn default() -> Self {96        Self::Normal97    }98}99100impl Default for CollectionMode {101    fn default() -> Self {102        Self::Invalid103    }104}105106#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]107#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]108pub enum SchemaVersion {109    ImageURL,110    Unique,111}112impl Default for SchemaVersion {113    fn default() -> Self {114        Self::ImageURL115    }116}117118#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]119#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]120pub struct Ownership<AccountId> {121    pub owner: AccountId,122    pub fraction: u128,123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct CollectionType<AccountId> {128    pub owner: AccountId,129    pub mode: CollectionMode,130    pub access: AccessMode,131    pub decimal_points: DecimalPoints,132    pub name: Vec<u16>,        // 64 include null escape char133    pub description: Vec<u16>, // 256 include null escape char134    pub token_prefix: Vec<u8>, // 16 include null escape char135    pub mint_mode: bool,136    pub offchain_schema: Vec<u8>,137    pub schema_version: SchemaVersion,138    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender139    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.140    pub limits: CollectionLimits, // Collection private restrictions 141    pub variable_on_chain_schema: Vec<u8>, //142    pub const_on_chain_schema: Vec<u8>, //143}144145#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]146#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]147pub struct NftItemType<AccountId> {148    pub collection: CollectionId,149    pub owner: AccountId,150    pub const_data: Vec<u8>,151    pub variable_data: Vec<u8>,152}153154#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]155#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]156pub struct FungibleItemType<AccountId> {157    pub collection: CollectionId,158    pub owner: AccountId,159    pub value: u128,160}161162#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]163#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]164pub struct ReFungibleItemType<AccountId> {165    pub collection: CollectionId,166    pub owner: Vec<Ownership<AccountId>>,167    pub const_data: Vec<u8>,168    pub variable_data: Vec<u8>,169}170171#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]172#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]173pub struct ApprovePermissions<AccountId> {174    pub approved: AccountId,175    pub amount: u128,176}177178#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]179#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]180pub struct VestingItem<AccountId, Moment> {181    pub sender: AccountId,182    pub recipient: AccountId,183    pub collection_id: CollectionId,184    pub item_id: TokenId,185    pub amount: u64,186    pub vesting_date: Moment,187}188189#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]190#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]191pub struct BasketItem<AccountId, BlockNumber> {192    pub address: AccountId,193    pub start_block: BlockNumber,194}195196#[derive(Encode, Decode, Debug, Clone, PartialEq)]197#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]198pub struct CollectionLimits {199    pub account_token_ownership_limit: u32,200    pub sponsored_data_size: u32,201    pub token_limit: u32,202203    // Timeouts for item types in passed blocks204    pub sponsor_transfer_timeout: u32,205}206207impl Default for CollectionLimits {208    fn default() -> CollectionLimits {209        CollectionLimits { 210            account_token_ownership_limit: 10_000_000, 211            token_limit: u32::max_value(),212            sponsored_data_size: u32::max_value(), 213            sponsor_transfer_timeout: 14400 }214    }215}216217#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]218#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]219pub struct ChainLimits {220    pub collection_numbers_limit: u32,221    pub account_token_ownership_limit: u32,222    pub collections_admins_limit: u64,223    pub custom_data_limit: u32,224225    // Timeouts for item types in passed blocks226    pub nft_sponsor_transfer_timeout: u32,227    pub fungible_sponsor_transfer_timeout: u32,228    pub refungible_sponsor_transfer_timeout: u32,229}230231pub trait WeightInfo {232	fn create_collection() -> Weight;233	fn destroy_collection() -> Weight;234	fn add_to_white_list() -> Weight;235	fn remove_from_white_list() -> Weight;236    fn set_public_access_mode() -> Weight;237    fn set_mint_permission() -> Weight;238    fn change_collection_owner() -> Weight;239    fn add_collection_admin() -> Weight;240    fn remove_collection_admin() -> Weight;241    fn set_collection_sponsor() -> Weight;242    fn confirm_sponsorship() -> Weight;243    fn remove_collection_sponsor() -> Weight;244    fn create_item(s: usize) -> Weight;245    fn burn_item() -> Weight;246    fn transfer() -> Weight;247    fn approve() -> Weight;248    fn transfer_from() -> Weight;249    fn set_offchain_schema() -> Weight;250    fn set_const_on_chain_schema() -> Weight;251    fn set_variable_on_chain_schema() -> Weight;252    fn set_variable_meta_data() -> Weight;253    fn enable_contract_sponsoring() -> Weight;254}255256#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]257#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]258pub struct CreateNftData {259    pub const_data: Vec<u8>,260    pub variable_data: Vec<u8>,261}262263#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]264#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]265pub struct CreateFungibleData {266}267268#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]269#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]270pub struct CreateReFungibleData {271    pub const_data: Vec<u8>,272    pub variable_data: Vec<u8>,273}274275#[derive(Encode, Decode, Debug, Clone, PartialEq)]276#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]277pub enum CreateItemData {278    NFT(CreateNftData),279    Fungible(CreateFungibleData),280    ReFungible(CreateReFungibleData),281}282283impl CreateItemData {284    pub fn len(&self) -> usize {285        let len = match self {286            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),287            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),288            _ => 0289        };290        291        return len;292    }293}294295impl From<CreateNftData> for CreateItemData {296    fn from(item: CreateNftData) -> Self {297        CreateItemData::NFT(item)298    }299}300301impl From<CreateReFungibleData> for CreateItemData {302    fn from(item: CreateReFungibleData) -> Self {303        CreateItemData::ReFungible(item)304    }305}306307impl From<CreateFungibleData> for CreateItemData {308    fn from(item: CreateFungibleData) -> Self {309        CreateItemData::Fungible(item)310    }311}312313314decl_error! {315	/// Error for non-fungible-token module.316	pub enum Error for Module<T: Trait> {317        /// Total collections bound exceeded.318        TotalCollectionsLimitExceeded,319		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.320        CollectionDecimalPointLimitExceeded, 321        /// Collection name can not be longer than 63 char.322        CollectionNameLimitExceeded, 323        /// Collection description can not be longer than 255 char.324        CollectionDescriptionLimitExceeded, 325        /// Token prefix can not be longer than 15 char.326        CollectionTokenPrefixLimitExceeded,327        /// This collection does not exist.328        CollectionNotFound,329        /// Item not exists.330        TokenNotFound,331        /// Arithmetic calculation overflow.332        NumOverflow,       333        /// Account already has admin role.334        AlreadyAdmin,  335        /// You do not own this collection.336        NoPermission,337        /// This address is not set as sponsor, use setCollectionSponsor first.338        ConfirmUnsetSponsorFail,339        /// Collection is not in mint mode.340        PublicMintingNotAllowed,341        /// Sender parameter and item owner must be equal.342        MustBeTokenOwner,343        /// Item balance not enough.344        TokenValueTooLow,345        /// Size of item is too large.346        NftSizeLimitExceeded,347        /// No approve found348        ApproveNotFound,349        /// Requested value more than approved.350        TokenValueNotEnough,351        /// Only approved addresses can call this method.352        ApproveRequired,353        /// Address is not in white list.354        AddresNotInWhiteList,355        /// Number of collection admins bound exceeded.356        CollectionAdminsLimitExceeded,357        /// Owned tokens by a single address bound exceeded.358        AddressOwnershipLimitExceeded,359        /// Length of items properties must be greater than 0.360        EmptyArgument,361        /// const_data exceeded data limit.362        TokenConstDataLimitExceeded,363        /// variable_data exceeded data limit.364        TokenVariableDataLimitExceeded,365        /// Not NFT item data used to mint in NFT collection.366        NotNftDataUsedToMintNftCollectionToken,367        /// Not Fungible item data used to mint in Fungible collection.368        NotFungibleDataUsedToMintFungibleCollectionToken,369        /// Not Re Fungible item data used to mint in Re Fungible collection.370        NotReFungibleDataUsedToMintReFungibleCollectionToken,371        /// Unexpected collection type.372        UnexpectedCollectionType,373        /// Can't store metadata in fungible tokens.374        CantStoreMetadataInFungibleTokens,375        /// Collection token limit exceeded376        CollectionTokenLimitExceeded,377        /// Account token limit exceeded per collection378        AccountTokenLimitExceeded,379        /// Collection limit bounds per collection exceeded380        CollectionLimitBoundsExceeded381	}382}383384pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {385    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;386387    /// Weight information for extrinsics in this pallet.388	type WeightInfo: WeightInfo;389}390391#[cfg(feature = "runtime-benchmarks")]392mod benchmarking;393394// #endregion395396decl_storage! {397    trait Store for Module<T: Trait> as Nft {398399        // Private members400        NextCollectionID: CollectionId;401        CreatedCollectionCount: u32;402        ChainVersion: u64;403        ItemListIndex: map hasher(identity) CollectionId => TokenId;404405        // Chain limits struct406        pub ChainLimit get(fn chain_limit) config(): ChainLimits;407408        // Bound counters409        CollectionCount: u32;410        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;411412        // Basic collections413        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;414        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;415        pub WhiteList get(fn white_list): map hasher(identity) CollectionId => Vec<T::AccountId>;416417        /// Balance owner per collection map418        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;419420        /// second parameter: item id + owner account id421        pub ApprovedList get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;422423        /// Item collections424        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;425        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => FungibleItemType<T::AccountId>;426        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;427428        /// Index list429        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;430431        /// Tokens transfer baskets432        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;433        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Vec<BasketItem<T::AccountId, T::BlockNumber>>;434        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;435436        // Contract Sponsorship and Ownership437        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;438        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;439        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;440        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;441    }442    add_extra_genesis {443        build(|config: &GenesisConfig<T>| {444            // Modification of storage445            for (_num, _c) in &config.collection {446                <Module<T>>::init_collection(_c);447            }448449            for (_num, _q, _i) in &config.nft_item_id {450                <Module<T>>::init_nft_token(_i);451            }452453            for (_num, _q, _i) in &config.fungible_item_id {454                <Module<T>>::init_fungible_token(_i);455            }456457            for (_num, _q, _i) in &config.refungible_item_id {458                <Module<T>>::init_refungible_token(_i);459            }460        })461    }462}463464decl_event!(465    pub enum Event<T>466    where467        AccountId = <T as system::Trait>::AccountId,468    {469        /// New collection was created470        /// 471        /// # Arguments472        /// 473        /// * collection_id: Globally unique identifier of newly created collection.474        /// 475        /// * mode: [CollectionMode] converted into u8.476        /// 477        /// * account_id: Collection owner.478        Created(CollectionId, u8, AccountId),479480        /// New item was created.481        /// 482        /// # Arguments483        /// 484        /// * collection_id: Id of the collection where item was created.485        /// 486        /// * item_id: Id of an item. Unique within the collection.487        ItemCreated(CollectionId, TokenId),488489        /// Collection item was burned.490        /// 491        /// # Arguments492        /// 493        /// collection_id.494        /// 495        /// item_id: Identifier of burned NFT.496        ItemDestroyed(CollectionId, TokenId),497    }498);499500decl_module! {501    pub struct Module<T: Trait> for enum Call where origin: T::Origin {502503        fn deposit_event() = default;504        type Error = Error<T>;505506        fn on_initialize(now: T::BlockNumber) -> Weight {507508            if ChainVersion::get() < 2509            {510                let value = NextCollectionID::get();511                CreatedCollectionCount::put(value);512                ChainVersion::put(2);513            }514515            0516        }517518        /// 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.519        /// 520        /// # Permissions521        /// 522        /// * Anyone.523        /// 524        /// # Arguments525        /// 526        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.527        /// 528        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.529        /// 530        /// * token_prefix: UTF-8 string with token prefix.531        /// 532        /// * mode: [CollectionMode] collection type and type dependent data.533        // returns collection ID534        #[weight = T::WeightInfo::create_collection()]535        pub fn create_collection(origin,536                                 collection_name: Vec<u16>,537                                 collection_description: Vec<u16>,538                                 token_prefix: Vec<u8>,539                                 mode: CollectionMode) -> DispatchResult {540541            // Anyone can create a collection542            let who = ensure_signed(origin)?;543544            let decimal_points = match mode {545                CollectionMode::Fungible(points) => points,546                CollectionMode::ReFungible(points) => points,547                _ => 0548            };549550            // bound Total number of collections551            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);552553            // check params554            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);555556            let mut name = collection_name.to_vec();557            name.push(0);558            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);559560            let mut description = collection_description.to_vec();561            description.push(0);562            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);563564            let mut prefix = token_prefix.to_vec();565            prefix.push(0);566            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);567568            // Generate next collection ID569            let next_id = CreatedCollectionCount::get()570                .checked_add(1)571                .ok_or(Error::<T>::NumOverflow)?;572573            // bound counter574            let total = CollectionCount::get()575                .checked_add(1)576                .ok_or(Error::<T>::NumOverflow)?;577578            CreatedCollectionCount::put(next_id);579            CollectionCount::put(total);580581            // Create new collection582            let new_collection = CollectionType {583                owner: who.clone(),584                name: name,585                mode: mode.clone(),586                mint_mode: false,587                access: AccessMode::Normal,588                description: description,589                decimal_points: decimal_points,590                token_prefix: prefix,591                offchain_schema: Vec::new(),592                schema_version: SchemaVersion::ImageURL,593                sponsor: T::AccountId::default(),594                sponsor_confirmed: false,595                variable_on_chain_schema: Vec::new(),596                const_on_chain_schema: Vec::new(),597                limits: CollectionLimits::default(),598            };599600            // Add new collection to map601            <Collection<T>>::insert(next_id, new_collection);602603            // call event604            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));605606            Ok(())607        }608609        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.610        /// 611        /// # Permissions612        /// 613        /// * Collection Owner.614        /// 615        /// # Arguments616        /// 617        /// * collection_id: collection to destroy.618        #[weight = T::WeightInfo::destroy_collection()]619        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {620621            let sender = ensure_signed(origin)?;622            Self::check_owner_permissions(collection_id, sender)?;623624            <AddressTokens<T>>::remove_prefix(collection_id);625            <ApprovedList<T>>::remove_prefix(collection_id);626            <Balance<T>>::remove_prefix(collection_id);627            <ItemListIndex>::remove(collection_id);628            <AdminList<T>>::remove(collection_id);629            <Collection<T>>::remove(collection_id);630            <WhiteList<T>>::remove(collection_id);631632            <NftItemList<T>>::remove_prefix(collection_id);633            <FungibleItemList<T>>::remove_prefix(collection_id);634            <ReFungibleItemList<T>>::remove_prefix(collection_id);635636            <NftTransferBasket<T>>::remove_prefix(collection_id);637            <FungibleTransferBasket<T>>::remove_prefix(collection_id);638            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);639640            if CollectionCount::get() > 0641            {642                // bound couter643                let total = CollectionCount::get()644                    .checked_sub(1)645                    .ok_or(Error::<T>::NumOverflow)?;646647                CollectionCount::put(total);648            }649650            Ok(())651        }652653        /// Add an address to white list.654        /// 655        /// # Permissions656        /// 657        /// * Collection Owner658        /// * Collection Admin659        /// 660        /// # Arguments661        /// 662        /// * collection_id.663        /// 664        /// * address.665        #[weight = T::WeightInfo::add_to_white_list()]666        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{667668            let sender = ensure_signed(origin)?;669            Self::check_owner_or_admin_permissions(collection_id, sender)?;670671            let mut white_list_collection: Vec<T::AccountId>;672            if <WhiteList<T>>::contains_key(collection_id) {673                white_list_collection = <WhiteList<T>>::get(collection_id);674                if !white_list_collection.contains(&address.clone())675                {676                    white_list_collection.push(address.clone());677                }678            }679            else {680                white_list_collection = Vec::new();681                white_list_collection.push(address.clone());682            }683684            <WhiteList<T>>::insert(collection_id, white_list_collection);685            Ok(())686        }687688        /// Remove an address from white list.689        /// 690        /// # Permissions691        /// 692        /// * Collection Owner693        /// * Collection Admin694        /// 695        /// # Arguments696        /// 697        /// * collection_id.698        /// 699        /// * address.700        #[weight = T::WeightInfo::remove_from_white_list()]701        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{702703            let sender = ensure_signed(origin)?;704            Self::check_owner_or_admin_permissions(collection_id, sender)?;705706            if <WhiteList<T>>::contains_key(collection_id) {707                let mut white_list_collection = <WhiteList<T>>::get(collection_id);708                if white_list_collection.contains(&address.clone())709                {710                    white_list_collection.retain(|i| *i != address.clone());711                    <WhiteList<T>>::insert(collection_id, white_list_collection);712                }713            }714715            Ok(())716        }717718        /// Toggle between normal and white list access for the methods with access for `Anyone`.719        /// 720        /// # Permissions721        /// 722        /// * Collection Owner.723        /// 724        /// # Arguments725        /// 726        /// * collection_id.727        /// 728        /// * mode: [AccessMode]729        #[weight = T::WeightInfo::set_public_access_mode()]730        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult731        {732            let sender = ensure_signed(origin)?;733734            Self::check_owner_permissions(collection_id, sender)?;735            let mut target_collection = <Collection<T>>::get(collection_id);736            target_collection.access = mode;737            <Collection<T>>::insert(collection_id, target_collection);738739            Ok(())740        }741742        /// Allows Anyone to create tokens if:743        /// * White List is enabled, and744        /// * Address is added to white list, and745        /// * This method was called with True parameter746        /// 747        /// # Permissions748        /// * Collection Owner749        ///750        /// # Arguments751        /// 752        /// * collection_id.753        /// 754        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.755        #[weight = T::WeightInfo::set_mint_permission()]756        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult757        {758            let sender = ensure_signed(origin)?;759760            Self::check_owner_permissions(collection_id, sender)?;761            let mut target_collection = <Collection<T>>::get(collection_id);762            target_collection.mint_mode = mint_permission;763            <Collection<T>>::insert(collection_id, target_collection);764765            Ok(())766        }767768        /// Change the owner of the collection.769        /// 770        /// # Permissions771        /// 772        /// * Collection Owner.773        /// 774        /// # Arguments775        /// 776        /// * collection_id.777        /// 778        /// * new_owner.779        #[weight = T::WeightInfo::change_collection_owner()]780        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {781782            let sender = ensure_signed(origin)?;783            Self::check_owner_permissions(collection_id, sender)?;784            let mut target_collection = <Collection<T>>::get(collection_id);785            target_collection.owner = new_owner;786            <Collection<T>>::insert(collection_id, target_collection);787788            Ok(())789        }790791        /// Adds an admin of the Collection.792        /// 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. 793        /// 794        /// # Permissions795        /// 796        /// * Collection Owner.797        /// * Collection Admin.798        /// 799        /// # Arguments800        /// 801        /// * collection_id: ID of the Collection to add admin for.802        /// 803        /// * new_admin_id: Address of new admin to add.804        #[weight = T::WeightInfo::add_collection_admin()]805        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {806807            let sender = ensure_signed(origin)?;808            Self::check_owner_or_admin_permissions(collection_id, sender)?;809            let mut admin_arr: Vec<T::AccountId> = Vec::new();810811            if <AdminList<T>>::contains_key(collection_id)812            {813                admin_arr = <AdminList<T>>::get(collection_id);814                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);815            }816817            // Number of collection admins818            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);819820            admin_arr.push(new_admin_id);821            <AdminList<T>>::insert(collection_id, admin_arr);822823            Ok(())824        }825826        /// 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.827        ///828        /// # Permissions829        /// 830        /// * Collection Owner.831        /// * Collection Admin.832        /// 833        /// # Arguments834        /// 835        /// * collection_id: ID of the Collection to remove admin for.836        /// 837        /// * account_id: Address of admin to remove.838        #[weight = T::WeightInfo::remove_collection_admin()]839        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {840841            let sender = ensure_signed(origin)?;842            Self::check_owner_or_admin_permissions(collection_id, sender)?;843844            if <AdminList<T>>::contains_key(collection_id)845            {846                let mut admin_arr = <AdminList<T>>::get(collection_id);847                admin_arr.retain(|i| *i != account_id);848                <AdminList<T>>::insert(collection_id, admin_arr);849            }850851            Ok(())852        }853854        /// # Permissions855        /// 856        /// * Collection Owner857        /// 858        /// # Arguments859        /// 860        /// * collection_id.861        /// 862        /// * new_sponsor.863        #[weight = T::WeightInfo::set_collection_sponsor()]864        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {865866            let sender = ensure_signed(origin)?;867            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);868869            let mut target_collection = <Collection<T>>::get(collection_id);870            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);871872            target_collection.sponsor = new_sponsor;873            target_collection.sponsor_confirmed = false;874            <Collection<T>>::insert(collection_id, target_collection);875876            Ok(())877        }878879        /// # Permissions880        /// 881        /// * Sponsor.882        /// 883        /// # Arguments884        /// 885        /// * collection_id.886        #[weight = T::WeightInfo::confirm_sponsorship()]887        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {888889            let sender = ensure_signed(origin)?;890            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);891892            let mut target_collection = <Collection<T>>::get(collection_id);893            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);894895            target_collection.sponsor_confirmed = true;896            <Collection<T>>::insert(collection_id, target_collection);897898            Ok(())899        }900901        /// Switch back to pay-per-own-transaction model.902        ///903        /// # Permissions904        ///905        /// * Collection owner.906        /// 907        /// # Arguments908        /// 909        /// * collection_id.910        #[weight = T::WeightInfo::remove_collection_sponsor()]911        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {912913            let sender = ensure_signed(origin)?;914            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);915916            let mut target_collection = <Collection<T>>::get(collection_id);917            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);918919            target_collection.sponsor = T::AccountId::default();920            target_collection.sponsor_confirmed = false;921            <Collection<T>>::insert(collection_id, target_collection);922923            Ok(())924        }925926        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.927        /// 928        /// # Permissions929        /// 930        /// * Collection Owner.931        /// * Collection Admin.932        /// * Anyone if933        ///     * White List is enabled, and934        ///     * Address is added to white list, and935        ///     * MintPermission is enabled (see SetMintPermission method)936        /// 937        /// # Arguments938        /// 939        /// * collection_id: ID of the collection.940        /// 941        /// * owner: Address, initial owner of the NFT.942        ///943        /// * data: Token data to store on chain.944        // #[weight =945        // (130_000_000 as Weight)946        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))947        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))948        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]949950        #[weight = T::WeightInfo::create_item(data.len())]951        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {952953            let sender = ensure_signed(origin)?;954955            Self::collection_exists(collection_id)?;956957            let target_collection = <Collection<T>>::get(collection_id);958959            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;960            Self::validate_create_item_args(&target_collection, &data)?;961            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;962963            Ok(())964        }965966        /// This method creates multiple instances of NFT Collection created with CreateCollection method.967        /// 968        /// # Permissions969        /// 970        /// * Collection Owner.971        /// * Collection Admin.972        /// * Anyone if973        ///     * White List is enabled, and974        ///     * Address is added to white list, and975        ///     * MintPermission is enabled (see SetMintPermission method)976        /// 977        /// # Arguments978        /// 979        /// * collection_id: ID of the collection.980        /// 981        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].982        /// 983        /// * owner: Address, initial owner of the NFT.984        #[weight = T::WeightInfo::create_item(items_data.into_iter()985                               .map(|data| { data.len() })986                               .sum())]987        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {988989            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);990            let sender = ensure_signed(origin)?;991992            Self::collection_exists(collection_id)?;993            let target_collection = <Collection<T>>::get(collection_id);994995            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;996997            for data in &items_data {998                Self::validate_create_item_args(&target_collection, data)?;999            }1000            for data in &items_data {1001                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;1002            }10031004            Ok(())1005        }10061007        /// Destroys a concrete instance of NFT.1008        /// 1009        /// # Permissions1010        /// 1011        /// * Collection Owner.1012        /// * Collection Admin.1013        /// * Current NFT Owner.1014        /// 1015        /// # Arguments1016        /// 1017        /// * collection_id: ID of the collection.1018        /// 1019        /// * item_id: ID of NFT to burn.1020        #[weight = T::WeightInfo::burn_item()]1021        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {10221023            let sender = ensure_signed(origin)?;1024            Self::collection_exists(collection_id)?;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            }10351036            match target_collection.mode1037            {1038                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1039                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,1040                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1041                _ => ()1042            };10431044            // call event1045            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10461047            Ok(())1048        }10491050        /// Change ownership of the token.1051        /// 1052        /// # Permissions1053        /// 1054        /// * Collection Owner1055        /// * Collection Admin1056        /// * Current NFT owner1057        ///1058        /// # Arguments1059        /// 1060        /// * recipient: Address of token recipient.1061        /// 1062        /// * collection_id.1063        /// 1064        /// * item_id: ID of the item1065        ///     * Non-Fungible Mode: Required.1066        ///     * Fungible Mode: Ignored.1067        ///     * Re-Fungible Mode: Required.1068        /// 1069        /// * value: Amount to transfer.1070        ///     * Non-Fungible Mode: Ignored1071        ///     * Fungible Mode: Must specify transferred amount1072        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1073        #[weight = T::WeightInfo::transfer()]1074        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10751076            let sender = ensure_signed(origin)?;1077            let target_collection = <Collection<T>>::get(collection_id);10781079            // Limits check1080            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;10811082            // Transfer permissions check1083            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1084                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1085                Error::<T>::NoPermission);10861087            if target_collection.access == AccessMode::WhiteList {1088                Self::check_white_list(collection_id, &sender)?;1089                Self::check_white_list(collection_id, &recipient)?;1090            }10911092            match target_collection.mode1093            {1094                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1095                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1096                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1097                _ => ()1098            };10991100            Ok(())1101        }11021103        /// Set, change, or remove approved address to transfer the ownership of the NFT.1104        /// 1105        /// # Permissions1106        /// 1107        /// * Collection Owner1108        /// * Collection Admin1109        /// * Current NFT owner1110        /// 1111        /// # Arguments1112        /// 1113        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1114        /// 1115        /// * collection_id.1116        /// 1117        /// * item_id: ID of the item.1118        #[weight = T::WeightInfo::approve()]1119        pub fn approve(origin, approved: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {11201121            let sender = ensure_signed(origin)?;11221123            // Transfer permissions check1124            let target_collection = <Collection<T>>::get(collection_id);1125            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1126                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1127                Error::<T>::NoPermission);11281129            if target_collection.access == AccessMode::WhiteList {1130                Self::check_white_list(collection_id, &sender)?;1131                Self::check_white_list(collection_id, &approved)?;1132            }11331134            // amount param stub1135            let amount = 100000000;11361137            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1138            if list_exists {11391140                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1141                let item_contains = list.iter().any(|i| i.approved == approved);11421143                if !item_contains {1144                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1145                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1146                }1147            } else {11481149                let mut list = Vec::new();1150                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1151                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1152            }11531154            Ok(())1155        }1156        1157        /// 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.1158        /// 1159        /// # Permissions1160        /// * Collection Owner1161        /// * Collection Admin1162        /// * Current NFT owner1163        /// * Address approved by current NFT owner1164        /// 1165        /// # Arguments1166        /// 1167        /// * from: Address that owns token.1168        /// 1169        /// * recipient: Address of token recipient.1170        /// 1171        /// * collection_id.1172        /// 1173        /// * item_id: ID of the item.1174        /// 1175        /// * value: Amount to transfer.1176        #[weight = T::WeightInfo::transfer_from()]1177        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11781179            let sender = ensure_signed(origin)?;1180            let mut appoved_transfer = false;11811182            // Check approve1183            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1184                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1185                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1186                if opt_item.is_some()1187                {1188                    appoved_transfer = true;1189                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1190                }1191            }11921193            let target_collection = <Collection<T>>::get(collection_id);11941195            // Limits check1196            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11971198            // Transfer permissions check         1199            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1200            Error::<T>::NoPermission);12011202            if target_collection.access == AccessMode::WhiteList {1203                Self::check_white_list(collection_id, &sender)?;1204                Self::check_white_list(collection_id, &recipient)?;1205            }12061207            // remove approve1208            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1209                .into_iter().filter(|i| i.approved != sender.clone()).collect();1210            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);121112121213            match target_collection.mode1214            {1215                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1216                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1217                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1218                _ => ()1219            };12201221            Ok(())1222        }12231224        #[weight = 0]1225        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {12261227            // let no_perm_mes = "You do not have permissions to modify this collection";1228            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1229            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1230            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);12311232            // // on_nft_received  call12331234            // Self::transfer(origin, collection_id, item_id, new_owner)?;12351236            Ok(())1237        }12381239        /// Set off-chain data schema.1240        /// 1241        /// # Permissions1242        /// 1243        /// * Collection Owner1244        /// * Collection Admin1245        /// 1246        /// # Arguments1247        /// 1248        /// * collection_id.1249        /// 1250        /// * schema: String representing the offchain data schema.1251        #[weight = T::WeightInfo::set_variable_meta_data()]1252        pub fn set_variable_meta_data (1253            origin,1254            collection_id: CollectionId,1255            item_id: TokenId,1256            data: Vec<u8>1257        ) -> DispatchResult {1258            let sender = ensure_signed(origin)?;1259            1260            Self::collection_exists(collection_id)?;1261            1262            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12631264            // Modify permissions check1265            let target_collection = <Collection<T>>::get(collection_id);1266            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1267                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1268                Error::<T>::NoPermission);12691270            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12711272            match target_collection.mode1273            {1274                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1275                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1276                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1277                _ => fail!(Error::<T>::UnexpectedCollectionType)1278            };12791280            Ok(())1281        }1282 1283        /// Set schema standard1284        /// ImageURL1285        /// Unique1286        /// 1287        /// # Permissions1288        /// 1289        /// * Collection Owner1290        /// * Collection Admin1291        /// 1292        /// # Arguments1293        /// 1294        /// * collection_id.1295        /// 1296        /// * schema: SchemaVersion: enum1297        #[weight = 0]1298        pub fn set_schema_version(1299            origin,1300            collection_id: CollectionId,1301            version: SchemaVersion1302        ) -> DispatchResult {1303            let sender = ensure_signed(origin)?;1304            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1305            let mut target_collection = <Collection<T>>::get(collection_id);1306            target_collection.schema_version = version;1307            <Collection<T>>::insert(collection_id, target_collection);13081309            Ok(())1310        }13111312        /// Set off-chain data schema.1313        /// 1314        /// # Permissions1315        /// 1316        /// * Collection Owner1317        /// * Collection Admin1318        /// 1319        /// # Arguments1320        /// 1321        /// * collection_id.1322        /// 1323        /// * schema: String representing the offchain data schema.1324        #[weight = T::WeightInfo::set_offchain_schema()]1325        pub fn set_offchain_schema(1326            origin,1327            collection_id: CollectionId,1328            schema: Vec<u8>1329        ) -> DispatchResult {1330            let sender = ensure_signed(origin)?;1331            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13321333            let mut target_collection = <Collection<T>>::get(collection_id);1334            target_collection.offchain_schema = schema;1335            <Collection<T>>::insert(collection_id, target_collection);13361337            Ok(())1338        }13391340        /// Set const on-chain data schema.1341        /// 1342        /// # Permissions1343        /// 1344        /// * Collection Owner1345        /// * Collection Admin1346        /// 1347        /// # Arguments1348        /// 1349        /// * collection_id.1350        /// 1351        /// * schema: String representing the const on-chain data schema.1352        #[weight = T::WeightInfo::set_const_on_chain_schema()]1353        pub fn set_const_on_chain_schema (1354            origin,1355            collection_id: CollectionId,1356            schema: Vec<u8>1357        ) -> DispatchResult {1358            let sender = ensure_signed(origin)?;1359            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13601361            let mut target_collection = <Collection<T>>::get(collection_id);1362            target_collection.const_on_chain_schema = schema;1363            <Collection<T>>::insert(collection_id, target_collection);13641365            Ok(())1366        }13671368        /// Set variable on-chain data schema.1369        /// 1370        /// # Permissions1371        /// 1372        /// * Collection Owner1373        /// * Collection Admin1374        /// 1375        /// # Arguments1376        /// 1377        /// * collection_id.1378        /// 1379        /// * schema: String representing the variable on-chain data schema.1380        #[weight = T::WeightInfo::set_const_on_chain_schema()]1381        pub fn set_variable_on_chain_schema (1382            origin,1383            collection_id: CollectionId,1384            schema: Vec<u8>1385        ) -> DispatchResult {1386            let sender = ensure_signed(origin)?;1387            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13881389            let mut target_collection = <Collection<T>>::get(collection_id);1390            target_collection.variable_on_chain_schema = schema;1391            <Collection<T>>::insert(collection_id, target_collection);13921393            Ok(())1394        }13951396        // Sudo permissions function1397        #[weight = 0]1398        pub fn set_chain_limits(1399            origin,1400            limits: ChainLimits1401        ) -> DispatchResult {1402            ensure_root(origin)?;1403            <ChainLimit>::put(limits);1404            Ok(())1405        }14061407        /// Enable smart contract self-sponsoring.1408        /// 1409        /// # Permissions1410        /// 1411        /// * Contract Owner1412        /// 1413        /// # Arguments1414        /// 1415        /// * contract address1416        /// * enable flag1417        /// 1418        #[weight = T::WeightInfo::enable_contract_sponsoring()]1419        pub fn enable_contract_sponsoring(1420            origin,1421            contract_address: T::AccountId,1422            enable: bool1423        ) -> DispatchResult {14241425            let sender = ensure_signed(origin)?;14261427            #[cfg(feature = "runtime-benchmarks")]1428            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14291430            let mut is_owner = false;1431            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1432                let owner = <ContractOwner<T>>::get(&contract_address);1433                is_owner = sender == owner;1434            }1435            ensure!(is_owner, Error::<T>::NoPermission);14361437            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1438            Ok(())1439        }14401441        /// Set the rate limit for contract sponsoring to specified number of blocks.1442        /// 1443        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1444        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1445        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1446        /// from contract endowment if there are at least B blocks between such transactions. 1447        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1448        /// 1449        /// # Permissions1450        /// 1451        /// * Contract Owner1452        /// 1453        /// # Arguments1454        /// 1455        /// -`contract_address`: Address of the contract to sponsor1456        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1457        /// 1458        #[weight = 0]1459        pub fn set_contract_sponsoring_rate_limit(1460            origin,1461            contract_address: T::AccountId,1462            rate_limit: T::BlockNumber1463        ) -> DispatchResult {1464            let sender = ensure_signed(origin)?;1465            let mut is_owner = false;1466            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1467                let owner = <ContractOwner<T>>::get(&contract_address);1468                is_owner = sender == owner;1469            }1470            ensure!(is_owner, Error::<T>::NoPermission);14711472            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1473            Ok(())1474        }14751476        #[weight = 0]1477        pub fn set_collection_limits(1478            origin,1479            collection_id: u32,1480            limits: CollectionLimits,1481        ) -> DispatchResult {1482            let sender = ensure_signed(origin)?;1483            Self::check_owner_permissions(collection_id, sender.clone())?;1484            let mut target_collection = <Collection<T>>::get(collection_id);1485            let chain_limits = ChainLimit::get();1486            let climits = target_collection.limits;14871488            // collection bounds1489            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1490                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1491                Error::<T>::CollectionLimitBoundsExceeded);14921493            // token_limit   check  prev1494            ensure!(climits.token_limit > limits.token_limit && 1495                limits.token_limit <= chain_limits.account_token_ownership_limit, 1496                Error::<T>::AccountTokenLimitExceeded);14971498            target_collection.limits = limits;1499            <Collection<T>>::insert(collection_id, target_collection);15001501            Ok(())1502        } 1503    }1504}15051506impl<T: Trait> Module<T> {15071508    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15091510        // check token limit and account token limit1511        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1512        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1513        1514        Ok(())1515    }15161517    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15181519        // check token limit and account token limit1520        let total_items: u32 = ItemListIndex::get(collection_id);1521        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1522        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1523        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15241525        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1526            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1527            Self::check_white_list(collection_id, owner)?;1528            Self::check_white_list(collection_id, sender)?;1529        }15301531        Ok(())1532    }15331534    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1535        match target_collection.mode1536        {1537            CollectionMode::NFT => {1538                if let CreateItemData::NFT(data) = data {1539                    // check sizes1540                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1541                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1542                } else {1543                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1544                }1545            },1546            CollectionMode::Fungible(_) => {1547                if let CreateItemData::Fungible(_) = data {1548                } else {1549                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1550                }1551            },1552            CollectionMode::ReFungible(_) => {1553                if let CreateItemData::ReFungible(data) = data {15541555                    // check sizes1556                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1557                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1558                } else {1559                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1560                }1561            },1562            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1563        };15641565        Ok(())1566    }15671568    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1569        match data1570        {1571            CreateItemData::NFT(data) => {1572                let item = NftItemType {1573                    collection: collection_id,1574                    owner,1575                    const_data: data.const_data,1576                    variable_data: data.variable_data1577                };15781579                Self::add_nft_item(item)?;1580            },1581            CreateItemData::Fungible(_) => {1582                let item = FungibleItemType {1583                    collection: collection_id,1584                    owner,1585                    value: (10 as u128).pow(collection.decimal_points as u32)1586                };15871588                Self::add_fungible_item(item)?;1589            },1590            CreateItemData::ReFungible(data) => {1591                let mut owner_list = Vec::new();1592                let value = (10 as u128).pow(collection.decimal_points as u32);1593                owner_list.push(Ownership {owner: owner.clone(), fraction: value});15941595                let item = ReFungibleItemType {1596                    collection: collection_id,1597                    owner: owner_list,1598                    const_data: data.const_data,1599                    variable_data: data.variable_data1600                };16011602                Self::add_refungible_item(item)?;1603            }1604        };16051606        // call event1607        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16081609        Ok(())1610    }16111612    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1613        let current_index = <ItemListIndex>::get(item.collection)1614            .checked_add(1)1615            .ok_or(Error::<T>::NumOverflow)?;1616        let itemcopy = item.clone();1617        let owner = item.owner.clone();16181619        Self::add_token_index(item.collection, current_index, owner.clone())?;16201621        <ItemListIndex>::insert(item.collection, current_index);1622        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);16231624        // Add current block1625        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1626        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1627        1628        // Update balance1629        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1630            .checked_add(item.value)1631            .ok_or(Error::<T>::NumOverflow)?;1632        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);16331634        Ok(())1635    }16361637    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1638        let current_index = <ItemListIndex>::get(item.collection)1639            .checked_add(1)1640            .ok_or(Error::<T>::NumOverflow)?;1641        let itemcopy = item.clone();16421643        let value = item.owner.first().unwrap().fraction;1644        let owner = item.owner.first().unwrap().owner.clone();16451646        Self::add_token_index(item.collection, current_index, owner.clone())?;16471648        <ItemListIndex>::insert(item.collection, current_index);1649        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);16501651        // Add current block1652        let block_number: T::BlockNumber = 0.into();1653        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);16541655        // Update balance1656        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1657            .checked_add(value)1658            .ok_or(Error::<T>::NumOverflow)?;1659        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);16601661        Ok(())1662    }16631664    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1665        let current_index = <ItemListIndex>::get(item.collection)1666            .checked_add(1)1667            .ok_or(Error::<T>::NumOverflow)?;16681669        let item_owner = item.owner.clone();1670        let collection_id = item.collection.clone();1671        Self::add_token_index(collection_id, current_index, item.owner.clone())?;16721673        <ItemListIndex>::insert(collection_id, current_index);1674        <NftItemList<T>>::insert(collection_id, current_index, item);16751676        // Add current block1677        let block_number: T::BlockNumber = 0.into();1678        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);16791680        // Update balance1681        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1682            .checked_add(1)1683            .ok_or(Error::<T>::NumOverflow)?;1684        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);16851686        Ok(())1687    }16881689    fn burn_refungible_item(1690        collection_id: CollectionId,1691        item_id: TokenId,1692        owner: T::AccountId,1693    ) -> DispatchResult {1694        ensure!(1695            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1696            Error::<T>::TokenNotFound1697        );1698        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1699        let item = collection1700            .owner1701            .iter()1702            .filter(|&i| i.owner == owner)1703            .next()1704            .unwrap();1705        Self::remove_token_index(collection_id, item_id, owner.clone())?;17061707        // remove approve list1708        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));17091710        // update balance1711        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1712            .checked_sub(item.fraction)1713            .ok_or(Error::<T>::NumOverflow)?;1714        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17151716        <ReFungibleItemList<T>>::remove(collection_id, item_id);17171718        Ok(())1719    }17201721    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1722        ensure!(1723            <NftItemList<T>>::contains_key(collection_id, item_id),1724            Error::<T>::TokenNotFound1725        );1726        let item = <NftItemList<T>>::get(collection_id, item_id);1727        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17281729        // remove approve list1730        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));17311732        // update balance1733        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1734            .checked_sub(1)1735            .ok_or(Error::<T>::NumOverflow)?;1736        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1737        <NftItemList<T>>::remove(collection_id, item_id);17381739        Ok(())1740    }17411742    fn burn_fungible_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1743        ensure!(1744            <FungibleItemList<T>>::contains_key(collection_id, item_id),1745            Error::<T>::TokenNotFound1746        );1747        let item = <FungibleItemList<T>>::get(collection_id, item_id);1748        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17491750        // remove approve list1751        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));17521753        // update balance1754        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1755            .checked_sub(item.value)1756            .ok_or(Error::<T>::NumOverflow)?;1757        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17581759        <FungibleItemList<T>>::remove(collection_id, item_id);17601761        Ok(())1762    }17631764    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1765        ensure!(1766            <Collection<T>>::contains_key(collection_id),1767            Error::<T>::CollectionNotFound1768        );1769        Ok(())1770    }17711772    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1773        Self::collection_exists(collection_id)?;17741775        let target_collection = <Collection<T>>::get(collection_id);1776        ensure!(1777            subject == target_collection.owner,1778            Error::<T>::NoPermission1779        );17801781        Ok(())1782    }17831784    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1785        let target_collection = <Collection<T>>::get(collection_id);1786        let mut result: bool = subject == target_collection.owner;1787        let exists = <AdminList<T>>::contains_key(collection_id);17881789        if !result & exists {1790            if <AdminList<T>>::get(collection_id).contains(&subject) {1791                result = true1792            }1793        }17941795        result1796    }17971798    fn check_owner_or_admin_permissions(1799        collection_id: CollectionId,1800        subject: T::AccountId,1801    ) -> DispatchResult {1802        Self::collection_exists(collection_id)?;1803        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18041805        ensure!(1806            result,1807            Error::<T>::NoPermission1808        );1809        Ok(())1810    }18111812    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1813        let target_collection = <Collection<T>>::get(collection_id);18141815        match target_collection.mode {1816            CollectionMode::NFT => {1817                <NftItemList<T>>::get(collection_id, item_id).owner == subject1818            }1819            CollectionMode::Fungible(_) => {1820                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1821            }1822            CollectionMode::ReFungible(_) => {1823                <ReFungibleItemList<T>>::get(collection_id, item_id)1824                    .owner1825                    .iter()1826                    .any(|i| i.owner == subject)1827            }1828            CollectionMode::Invalid => false,1829        }1830    }18311832    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1833        let mes = Error::<T>::AddresNotInWhiteList;1834        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1835        let wl = <WhiteList<T>>::get(collection_id);1836        ensure!(wl.contains(address), mes);18371838        Ok(())1839    }18401841    fn transfer_fungible(1842        collection_id: CollectionId,1843        item_id: TokenId,1844        value: u128,1845        owner: T::AccountId,1846        new_owner: T::AccountId,1847    ) -> DispatchResult {1848        ensure!(1849            <FungibleItemList<T>>::contains_key(collection_id, item_id),1850            Error::<T>::TokenNotFound1851        );18521853        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1854        let amount = full_item.value;18551856        ensure!(amount >= value, Error::<T>::TokenValueTooLow);18571858        // update balance1859        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1860            .checked_sub(value)1861            .ok_or(Error::<T>::NumOverflow)?;1862        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);18631864        let mut new_owner_account_id = 0;1865        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1866        if new_owner_items.len() > 0 {1867            new_owner_account_id = new_owner_items[0];1868        }18691870        // transfer1871        if amount == value && new_owner_account_id == 0 {1872            // change owner1873            // new owner do not have account1874            let mut new_full_item = full_item.clone();1875            new_full_item.owner = new_owner.clone();1876            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18771878            // update balance1879            let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1880                .checked_add(value)1881                .ok_or(Error::<T>::NumOverflow)?;1882            <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);18831884            // update index collection1885            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1886        } else {1887            let mut new_full_item = full_item.clone();1888            new_full_item.value -= value;18891890            // separate amount1891            if new_owner_account_id > 0 {1892                // new owner has account1893                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1894                item.value += value;18951896                // update balance1897                let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1898                    .checked_add(value)1899                    .ok_or(Error::<T>::NumOverflow)?;1900                <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19011902                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1903            } else {1904                // new owner do not have account1905                let item = FungibleItemType {1906                    collection: collection_id,1907                    owner: new_owner.clone(),1908                    value1909                };19101911                Self::add_fungible_item(item)?;1912            }19131914            if amount == value {1915                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;19161917                // remove approve list1918                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1919                <FungibleItemList<T>>::remove(collection_id, item_id);1920            }19211922            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1923        }19241925        Ok(())1926    }19271928    fn transfer_refungible(1929        collection_id: CollectionId,1930        item_id: TokenId,1931        value: u128,1932        owner: T::AccountId,1933        new_owner: T::AccountId,1934    ) -> DispatchResult {1935        ensure!(1936            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1937            Error::<T>::TokenNotFound1938        );19391940        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1941        let item = full_item1942            .owner1943            .iter()1944            .filter(|i| i.owner == owner)1945            .next()1946            .ok_or(Error::<T>::NumOverflow)?;1947        let amount = item.fraction;19481949        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19501951        // update balance1952        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1953            .checked_sub(value)1954            .ok_or(Error::<T>::NumOverflow)?;1955        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19561957        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1958            .checked_add(value)1959            .ok_or(Error::<T>::NumOverflow)?;1960        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19611962        let old_owner = item.owner.clone();1963        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19641965        // transfer1966        if amount == value && !new_owner_has_account {1967            // change owner1968            // new owner do not have account1969            let mut new_full_item = full_item.clone();1970            new_full_item1971                .owner1972                .iter_mut()1973                .find(|i| i.owner == owner)1974                .unwrap()1975                .owner = new_owner.clone();1976            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19771978            // update index collection1979            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1980        } else {1981            let mut new_full_item = full_item.clone();1982            new_full_item1983                .owner1984                .iter_mut()1985                .find(|i| i.owner == owner)1986                .unwrap()1987                .fraction -= value;19881989            // separate amount1990            if new_owner_has_account {1991                // new owner has account1992                new_full_item1993                    .owner1994                    .iter_mut()1995                    .find(|i| i.owner == new_owner)1996                    .unwrap()1997                    .fraction += value;1998            } else {1999                // new owner do not have account2000                new_full_item.owner.push(Ownership {2001                    owner: new_owner.clone(),2002                    fraction: value,2003                });2004                Self::add_token_index(collection_id, item_id, new_owner.clone())?;2005            }20062007            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2008        }20092010        Ok(())2011    }20122013    fn transfer_nft(2014        collection_id: CollectionId,2015        item_id: TokenId,2016        sender: T::AccountId,2017        new_owner: T::AccountId,2018    ) -> DispatchResult {2019        ensure!(2020            <NftItemList<T>>::contains_key(collection_id, item_id),2021            Error::<T>::TokenNotFound2022        );20232024        let mut item = <NftItemList<T>>::get(collection_id, item_id);20252026        ensure!(2027            sender == item.owner,2028            Error::<T>::MustBeTokenOwner2029        );20302031        // update balance2032        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2033            .checked_sub(1)2034            .ok_or(Error::<T>::NumOverflow)?;2035        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20362037        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2038            .checked_add(1)2039            .ok_or(Error::<T>::NumOverflow)?;2040        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20412042        // change owner2043        let old_owner = item.owner.clone();2044        item.owner = new_owner.clone();2045        <NftItemList<T>>::insert(collection_id, item_id, item);20462047        // update index collection2048        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;20492050        // reset approved list2051        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));2052        Ok(())2053    }2054    2055    fn item_exists(2056        collection_id: CollectionId,2057        item_id: TokenId,2058        mode: &CollectionMode2059    ) -> DispatchResult {2060        match mode {2061            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2062            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2063            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2064            _ => ()2065        };2066        2067        Ok(())2068    }20692070    fn set_re_fungible_variable_data(2071        collection_id: CollectionId,2072        item_id: TokenId,2073        data: Vec<u8>2074    ) -> DispatchResult {2075        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20762077        item.variable_data = data;20782079        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20802081        Ok(())2082    }20832084    fn set_nft_variable_data(2085        collection_id: CollectionId,2086        item_id: TokenId,2087        data: Vec<u8>2088    ) -> DispatchResult {2089        let mut item = <NftItemList<T>>::get(collection_id, item_id);2090        2091        item.variable_data = data;20922093        <NftItemList<T>>::insert(collection_id, item_id, item);2094        2095        Ok(())2096    }20972098    fn init_collection(item: &CollectionType<T::AccountId>) {2099        // check params2100        assert!(2101            item.decimal_points <= MAX_DECIMAL_POINTS,2102            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2103        );2104        assert!(2105            item.name.len() <= 64,2106            "Collection name can not be longer than 63 char"2107        );2108        assert!(2109            item.name.len() <= 256,2110            "Collection description can not be longer than 255 char"2111        );2112        assert!(2113            item.token_prefix.len() <= 16,2114            "Token prefix can not be longer than 15 char"2115        );21162117        // Generate next collection ID2118        let next_id = CreatedCollectionCount::get()2119            .checked_add(1)2120            .unwrap();21212122        CreatedCollectionCount::put(next_id);2123    }21242125    fn init_nft_token(item: &NftItemType<T::AccountId>) {2126        let current_index = <ItemListIndex>::get(item.collection)2127            .checked_add(1)2128            .unwrap();21292130        let item_owner = item.owner.clone();2131        let collection_id = item.collection.clone();2132        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();21332134        <ItemListIndex>::insert(collection_id, current_index);21352136        // Update balance2137        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2138            .checked_add(1)2139            .unwrap();2140        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2141    }21422143    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2144        let current_index = <ItemListIndex>::get(item.collection)2145            .checked_add(1)2146            .unwrap();2147        let owner = item.owner.clone();21482149        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();21502151        <ItemListIndex>::insert(item.collection, current_index);21522153        // Update balance2154        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2155            .checked_add(item.value)2156            .unwrap();2157        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2158    }21592160    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2161        let current_index = <ItemListIndex>::get(item.collection)2162            .checked_add(1)2163            .unwrap();21642165        let value = item.owner.first().unwrap().fraction;2166        let owner = item.owner.first().unwrap().owner.clone();21672168        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();21692170        <ItemListIndex>::insert(item.collection, current_index);21712172        // Update balance2173        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2174            .checked_add(value)2175            .unwrap();2176        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2177    }21782179    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {21802181        // add to account limit2182        if <AccountItemCount<T>>::contains_key(owner.clone()) {21832184            // bound Owned tokens by a single address2185            let count = <AccountItemCount<T>>::get(owner.clone());2186            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21872188            <AccountItemCount<T>>::insert(owner.clone(), count2189                .checked_add(1)2190                .ok_or(Error::<T>::NumOverflow)?);2191        }2192        else {2193            <AccountItemCount<T>>::insert(owner.clone(), 1);2194        }21952196        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2197        if list_exists {2198            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2199            let item_contains = list.contains(&item_index.clone());22002201            if !item_contains {2202                list.push(item_index.clone());2203            }22042205            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2206        } else {2207            let mut itm = Vec::new();2208            itm.push(item_index.clone());2209            <AddressTokens<T>>::insert(collection_id, owner, itm);2210            2211        }22122213        Ok(())2214    }22152216    fn remove_token_index(2217        collection_id: CollectionId,2218        item_index: TokenId,2219        owner: T::AccountId,2220    ) -> DispatchResult {22212222        // update counter2223        <AccountItemCount<T>>::insert(owner.clone(), 2224            <AccountItemCount<T>>::get(owner.clone())2225            .checked_sub(1)2226            .ok_or(Error::<T>::NumOverflow)?);222722282229        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2230        if list_exists {2231            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2232            let item_contains = list.contains(&item_index.clone());22332234            if item_contains {2235                list.retain(|&item| item != item_index);2236                <AddressTokens<T>>::insert(collection_id, owner, list);2237            }2238        }22392240        Ok(())2241    }22422243    fn move_token_index(2244        collection_id: CollectionId,2245        item_index: TokenId,2246        old_owner: T::AccountId,2247        new_owner: T::AccountId,2248    ) -> DispatchResult {2249        Self::remove_token_index(collection_id, item_index, old_owner)?;2250        Self::add_token_index(collection_id, item_index, new_owner)?;22512252        Ok(())2253    }2254}22552256////////////////////////////////////////////////////////////////////////////////////////////////////2257// Economic models2258// #region22592260/// Fee multiplier.2261pub type Multiplier = FixedU128;22622263type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2264    <T as system::Trait>::AccountId,2265>>::Balance;2266type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2267    <T as system::Trait>::AccountId,2268>>::NegativeImbalance;22692270/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2271/// in the queue.2272#[derive(Encode, Decode, Clone, Eq, PartialEq)]2273pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2274    #[codec(compact)] BalanceOf<T>2275);22762277impl<T: Trait + Send + Sync> sp_std::fmt::Debug2278    for ChargeTransactionPayment<T>2279{2280    #[cfg(feature = "std")]2281    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2282        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2283    }2284    #[cfg(not(feature = "std"))]2285    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2286        Ok(())2287    }2288}22892290impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2291where2292    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2293    BalanceOf<T>: Send + Sync + FixedPointOperand,2294{2295    /// utility constructor. Used only in client/factory code.2296    pub fn from(fee: BalanceOf<T>) -> Self {2297        Self(fee)2298    }22992300    pub fn traditional_fee(2301        len: usize,2302        info: &DispatchInfoOf<T::Call>,2303        tip: BalanceOf<T>,2304    ) -> BalanceOf<T>2305    where2306        T::Call: Dispatchable<Info = DispatchInfo>,2307    {2308        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2309    }23102311	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2312		let weight_saturation = T::MaximumBlockWeight::get() / info.weight.max(1);2313		let len_saturation = T::MaximumBlockLength::get() as u64 / (len as u64).max(1);2314		let coefficient: BalanceOf<T> = weight_saturation.min(len_saturation).saturated_into::<BalanceOf<T>>();2315		final_fee.saturating_mul(coefficient).saturated_into::<TransactionPriority>()2316	}23172318    fn withdraw_fee(2319        &self,2320        who: &T::AccountId,2321        call: &T::Call,2322        info: &DispatchInfoOf<T::Call>,2323        len: usize,2324    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2325        let tip = self.0;23262327        // Set fee based on call type. Creating collection costs 1 Unique.2328        // All other transactions have traditional fees so far2329        // let fee = match call.is_sub_type() {2330        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2331        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2332        //                                                 // _ => <BalanceOf<T>>::from(100)2333        // };2334        let fee = Self::traditional_fee(len, info, tip);23352336        // Only mess with balances if fee is not zero.2337        if fee.is_zero() {2338            return Ok((fee, None));2339        }23402341        // Determine who is paying transaction fee based on ecnomic model2342        // Parse call to extract collection ID and access collection sponsor2343        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2344            Some(Call::create_item(collection_id, _owner, _properties)) => {23452346                // check free create limit2347                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2348                   (<Collection<T>>::get(collection_id).sponsor_confirmed)2349                {2350                    <Collection<T>>::get(collection_id).sponsor2351                } else {2352                    T::AccountId::default()2353                }2354            }2355            Some(Call::transfer(new_owner, collection_id, item_id, _value)) => {2356                2357                let mut sponsor_transfer = false;2358                if <Collection<T>>::get(collection_id).sponsor_confirmed {23592360                    let collection_limits = <Collection<T>>::get(collection_id).limits;2361                    let collection_mode = <Collection<T>>::get(collection_id).mode;2362    2363                    // sponsor timeout2364                    sponsor_transfer = match collection_mode {2365                        CollectionMode::NFT => {2366    2367                            // get correct limit2368                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2369                                collection_limits.sponsor_transfer_timeout2370                            } else {2371                                ChainLimit::get().nft_sponsor_transfer_timeout2372                            };2373    2374                            let basket = <NftTransferBasket<T>>::get(collection_id, item_id);2375                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2376                            let limit_time = basket + limit.into();2377                            if block_number >= limit_time {2378                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2379                                true2380                            }2381                            else {2382                                false2383                            }2384                        }2385                        CollectionMode::Fungible(_) => {2386    2387                            // get correct limit2388                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2389                                collection_limits.sponsor_transfer_timeout2390                            } else {2391                                ChainLimit::get().fungible_sponsor_transfer_timeout2392                            };2393    2394                            let mut basket = <FungibleTransferBasket<T>>::get(collection_id, item_id);2395                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2396                            if basket.iter().any(|i| i.address == new_owner.clone())2397                            {2398                                let item = basket.iter_mut().find(|i| i.address == new_owner.clone()).unwrap().clone();2399                                let limit_time = item.start_block + limit.into();2400                                if block_number >= limit_time {2401                                    basket.retain(|x| x.address == item.address);2402                                    basket.push(BasketItem { start_block: block_number, address: new_owner.clone() });2403                                    <FungibleTransferBasket<T>>::insert(collection_id, item_id, basket);2404                                    true2405                                }2406                                else {2407                                    false2408                                }2409                            }2410                            else {2411                                basket.push(BasketItem { start_block: block_number, address: new_owner.clone()});2412                                true2413                            }2414                        }2415                        CollectionMode::ReFungible(_) => {2416    2417                            // get correct limit2418                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2419                                collection_limits.sponsor_transfer_timeout2420                            } else {2421                                ChainLimit::get().refungible_sponsor_transfer_timeout2422                            };2423    2424                            let basket = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2425                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2426                            let limit_time = basket + limit.into();2427                            if block_number >= limit_time {2428                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2429                                true2430                            } else {2431                                false2432                            }2433                        }2434                        _ => {2435                            false2436                        },2437                    };2438                }24392440                if !sponsor_transfer {2441                    T::AccountId::default()2442                } else {2443                    <Collection<T>>::get(collection_id).sponsor2444                }2445            }24462447            _ => T::AccountId::default(),2448        };24492450        // Sponsor smart contracts2451        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {24522453            // On instantiation: set the contract owner2454            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {24552456                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2457                    code_hash,2458                    &data,2459                    &who,2460                );2461                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());24622463                T::AccountId::default()2464            },24652466            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2467            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {24682469                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());24702471                let mut sponsor_transfer = false;2472                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2473                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2474                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2475                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2476                    let limit_time = last_tx_block + rate_limit;24772478                    if block_number >= limit_time {2479                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2480                        sponsor_transfer = true;2481                    }2482                } else {2483                    sponsor_transfer = false;2484                }2485               2486                2487                let mut sp = T::AccountId::default();2488                if sponsor_transfer {2489                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2490                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2491                            sp = called_contract;2492                        }2493                    }2494                }24952496                sp2497            },24982499            _ => sponsor,2500        };25012502        let mut who_pays_fee: T::AccountId = sponsor.clone();2503        if sponsor == T::AccountId::default() {2504            who_pays_fee = who.clone();2505        }25062507        match <T as transaction_payment::Trait>::Currency::withdraw(2508            &who_pays_fee,2509            fee,2510            if tip.is_zero() {2511                WithdrawReason::TransactionPayment.into()2512            } else {2513                WithdrawReason::TransactionPayment | WithdrawReason::Tip2514            },2515            ExistenceRequirement::KeepAlive,2516        ) {2517            Ok(imbalance) => Ok((fee, Some(imbalance))),2518            Err(_) => Err(InvalidTransaction::Payment.into()),2519        }2520    }2521}252225232524impl<T: Trait + Send + Sync> SignedExtension2525    for ChargeTransactionPayment<T>2526where2527    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2528    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2529{2530    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2531    type AccountId = T::AccountId;2532    type Call = T::Call;2533    type AdditionalSigned = ();2534    type Pre = (2535        BalanceOf<T>,2536        Self::AccountId,2537        Option<NegativeImbalanceOf<T>>,2538        BalanceOf<T>,2539    );2540    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2541        Ok(())2542    }25432544    fn validate(2545        &self,2546        who: &Self::AccountId,2547        call: &Self::Call,2548        info: &DispatchInfoOf<Self::Call>,2549        len: usize,2550    ) -> TransactionValidity {2551		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2552		Ok(ValidTransaction {2553			priority: Self::get_priority(len, info, fee),2554			..Default::default()2555		})2556    }25572558    fn pre_dispatch(2559        self,2560        who: &Self::AccountId,2561        call: &Self::Call,2562        info: &DispatchInfoOf<Self::Call>,2563        len: usize,2564    ) -> Result<Self::Pre, TransactionValidityError> {2565        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2566        Ok((self.0, who.clone(), imbalance, fee))2567    }25682569    fn post_dispatch(2570        pre: Self::Pre,2571        info: &DispatchInfoOf<Self::Call>,2572        post_info: &PostDispatchInfoOf<Self::Call>,2573        len: usize,2574        _result: &DispatchResult,2575    ) -> Result<(), TransactionValidityError> {2576        let (tip, who, imbalance, fee) = pre;2577        if let Some(payed) = imbalance {2578            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2579                len as u32, info, post_info, tip,2580            );2581            let refund = fee.saturating_sub(actual_fee);2582            let actual_payment =2583                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2584                    &who, refund,2585                ) {2586                    Ok(refund_imbalance) => {2587                        // The refund cannot be larger than the up front payed max weight.2588                        // `PostDispatchInfo::calc_unspent` guards against such a case.2589                        match payed.offset(refund_imbalance) {2590                            Ok(actual_payment) => actual_payment,2591                            Err(_) => return Err(InvalidTransaction::Payment.into()),2592                        }2593                    }2594                    // We do not recreate the account using the refund. The up front payment2595                    // is gone in that case.2596                    Err(_) => payed,2597                };2598            let imbalances = actual_payment.split(tip);2599            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2600                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2601            );2602        }2603        Ok(())2604    }2605}26062607// #endregion
after · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, WithdrawReason,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial,29    },30    IsSubType, StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use pallet_contracts::ContractAddressFor;45use sp_runtime::traits::StaticLookup;4647#[cfg(test)]48mod mock;4950#[cfg(test)]51mod tests;5253mod default_weights;5455pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;56pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;57pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;5859// Structs60// #region6162pub type CollectionId = u32;63pub type TokenId = u32;64pub type DecimalPoints = u8;6566#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]67#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]68pub enum CollectionMode {69    Invalid,70    NFT,71    // decimal points72    Fungible(DecimalPoints),73    // decimal points74    ReFungible(DecimalPoints),75}7677impl Into<u8> for CollectionMode {78    fn into(self) -> u8 {79        match self {80            CollectionMode::Invalid => 0,81            CollectionMode::NFT => 1,82            CollectionMode::Fungible(_) => 2,83            CollectionMode::ReFungible(_) => 3,84        }85    }86}8788#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]89#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]90pub enum AccessMode {91    Normal,92    WhiteList,93}94impl Default for AccessMode {95    fn default() -> Self {96        Self::Normal97    }98}99100impl Default for CollectionMode {101    fn default() -> Self {102        Self::Invalid103    }104}105106#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]107#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]108pub enum SchemaVersion {109    ImageURL,110    Unique,111}112impl Default for SchemaVersion {113    fn default() -> Self {114        Self::ImageURL115    }116}117118#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]119#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]120pub struct Ownership<AccountId> {121    pub owner: AccountId,122    pub fraction: u128,123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct CollectionType<AccountId> {128    pub owner: AccountId,129    pub mode: CollectionMode,130    pub access: AccessMode,131    pub decimal_points: DecimalPoints,132    pub name: Vec<u16>,        // 64 include null escape char133    pub description: Vec<u16>, // 256 include null escape char134    pub token_prefix: Vec<u8>, // 16 include null escape char135    pub mint_mode: bool,136    pub offchain_schema: Vec<u8>,137    pub schema_version: SchemaVersion,138    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender139    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.140    pub limits: CollectionLimits, // Collection private restrictions 141    pub variable_on_chain_schema: Vec<u8>, //142    pub const_on_chain_schema: Vec<u8>, //143}144145#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]146#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]147pub struct NftItemType<AccountId> {148    pub owner: AccountId,149    pub const_data: Vec<u8>,150    pub variable_data: Vec<u8>,151}152153#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]154#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]155pub struct FungibleItemType<AccountId> {156    pub owner: AccountId,157    pub value: u128,158}159160#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]161#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]162pub struct ReFungibleItemType<AccountId> {163    pub owner: Vec<Ownership<AccountId>>,164    pub const_data: Vec<u8>,165    pub variable_data: Vec<u8>,166}167168#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]169#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170pub struct ApprovePermissions<AccountId> {171    pub approved: AccountId,172    pub amount: u128,173}174175// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]176// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]177// pub struct VestingItem<AccountId, Moment> {178//     pub sender: AccountId,179//     pub recipient: AccountId,180//     pub collection_id: CollectionId,181//     pub item_id: TokenId,182//     pub amount: u64,183//     pub vesting_date: Moment,184// }185186#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]187#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]188pub struct BasketItem<AccountId, BlockNumber> {189    pub address: AccountId,190    pub start_block: BlockNumber,191}192193#[derive(Encode, Decode, Debug, Clone, PartialEq)]194#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]195pub struct CollectionLimits {196    pub account_token_ownership_limit: u32,197    pub sponsored_data_size: u32,198    pub token_limit: u32,199200    // Timeouts for item types in passed blocks201    pub sponsor_transfer_timeout: u32,202}203204impl Default for CollectionLimits {205    fn default() -> CollectionLimits {206        CollectionLimits { 207            account_token_ownership_limit: 10_000_000, 208            token_limit: u32::max_value(),209            sponsored_data_size: u32::max_value(), 210            sponsor_transfer_timeout: 14400 }211    }212}213214#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]215#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]216pub struct ChainLimits {217    pub collection_numbers_limit: u32,218    pub account_token_ownership_limit: u32,219    pub collections_admins_limit: u64,220    pub custom_data_limit: u32,221222    // Timeouts for item types in passed blocks223    pub nft_sponsor_transfer_timeout: u32,224    pub fungible_sponsor_transfer_timeout: u32,225    pub refungible_sponsor_transfer_timeout: u32,226}227228pub trait WeightInfo {229	fn create_collection() -> Weight;230	fn destroy_collection() -> Weight;231	fn add_to_white_list() -> Weight;232	fn remove_from_white_list() -> Weight;233    fn set_public_access_mode() -> Weight;234    fn set_mint_permission() -> Weight;235    fn change_collection_owner() -> Weight;236    fn add_collection_admin() -> Weight;237    fn remove_collection_admin() -> Weight;238    fn set_collection_sponsor() -> Weight;239    fn confirm_sponsorship() -> Weight;240    fn remove_collection_sponsor() -> Weight;241    fn create_item(s: usize) -> Weight;242    fn burn_item() -> Weight;243    fn transfer() -> Weight;244    fn approve() -> Weight;245    fn transfer_from() -> Weight;246    fn set_offchain_schema() -> Weight;247    fn set_const_on_chain_schema() -> Weight;248    fn set_variable_on_chain_schema() -> Weight;249    fn set_variable_meta_data() -> Weight;250    fn enable_contract_sponsoring() -> Weight;251}252253#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]254#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]255pub struct CreateNftData {256    pub const_data: Vec<u8>,257    pub variable_data: Vec<u8>,258}259260#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]261#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]262pub struct CreateFungibleData {263}264265#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]266#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]267pub struct CreateReFungibleData {268    pub const_data: Vec<u8>,269    pub variable_data: Vec<u8>,270}271272#[derive(Encode, Decode, Debug, Clone, PartialEq)]273#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]274pub enum CreateItemData {275    NFT(CreateNftData),276    Fungible(CreateFungibleData),277    ReFungible(CreateReFungibleData),278}279280impl CreateItemData {281    pub fn len(&self) -> usize {282        let len = match self {283            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),284            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),285            _ => 0286        };287        288        return len;289    }290}291292impl From<CreateNftData> for CreateItemData {293    fn from(item: CreateNftData) -> Self {294        CreateItemData::NFT(item)295    }296}297298impl From<CreateReFungibleData> for CreateItemData {299    fn from(item: CreateReFungibleData) -> Self {300        CreateItemData::ReFungible(item)301    }302}303304impl From<CreateFungibleData> for CreateItemData {305    fn from(item: CreateFungibleData) -> Self {306        CreateItemData::Fungible(item)307    }308}309310311decl_error! {312	/// Error for non-fungible-token module.313	pub enum Error for Module<T: Trait> {314        /// Total collections bound exceeded.315        TotalCollectionsLimitExceeded,316		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.317        CollectionDecimalPointLimitExceeded, 318        /// Collection name can not be longer than 63 char.319        CollectionNameLimitExceeded, 320        /// Collection description can not be longer than 255 char.321        CollectionDescriptionLimitExceeded, 322        /// Token prefix can not be longer than 15 char.323        CollectionTokenPrefixLimitExceeded,324        /// This collection does not exist.325        CollectionNotFound,326        /// Item not exists.327        TokenNotFound,328        /// Arithmetic calculation overflow.329        NumOverflow,       330        /// Account already has admin role.331        AlreadyAdmin,  332        /// You do not own this collection.333        NoPermission,334        /// This address is not set as sponsor, use setCollectionSponsor first.335        ConfirmUnsetSponsorFail,336        /// Collection is not in mint mode.337        PublicMintingNotAllowed,338        /// Sender parameter and item owner must be equal.339        MustBeTokenOwner,340        /// Item balance not enough.341        TokenValueTooLow,342        /// Size of item is too large.343        NftSizeLimitExceeded,344        /// No approve found345        ApproveNotFound,346        /// Requested value more than approved.347        TokenValueNotEnough,348        /// Only approved addresses can call this method.349        ApproveRequired,350        /// Address is not in white list.351        AddresNotInWhiteList,352        /// Number of collection admins bound exceeded.353        CollectionAdminsLimitExceeded,354        /// Owned tokens by a single address bound exceeded.355        AddressOwnershipLimitExceeded,356        /// Length of items properties must be greater than 0.357        EmptyArgument,358        /// const_data exceeded data limit.359        TokenConstDataLimitExceeded,360        /// variable_data exceeded data limit.361        TokenVariableDataLimitExceeded,362        /// Not NFT item data used to mint in NFT collection.363        NotNftDataUsedToMintNftCollectionToken,364        /// Not Fungible item data used to mint in Fungible collection.365        NotFungibleDataUsedToMintFungibleCollectionToken,366        /// Not Re Fungible item data used to mint in Re Fungible collection.367        NotReFungibleDataUsedToMintReFungibleCollectionToken,368        /// Unexpected collection type.369        UnexpectedCollectionType,370        /// Can't store metadata in fungible tokens.371        CantStoreMetadataInFungibleTokens,372        /// Collection token limit exceeded373        CollectionTokenLimitExceeded,374        /// Account token limit exceeded per collection375        AccountTokenLimitExceeded,376        /// Collection limit bounds per collection exceeded377        CollectionLimitBoundsExceeded378	}379}380381pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {382    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;383384    /// Weight information for extrinsics in this pallet.385	type WeightInfo: WeightInfo;386}387388#[cfg(feature = "runtime-benchmarks")]389mod benchmarking;390391// #endregion392393decl_storage! {394    trait Store for Module<T: Trait> as Nft {395396        // Private members397        NextCollectionID: CollectionId;398        CreatedCollectionCount: u32;399        ChainVersion: u64;400        ItemListIndex: map hasher(identity) CollectionId => TokenId;401402        // Chain limits struct403        pub ChainLimit get(fn chain_limit) config(): ChainLimits;404405        // Bound counters406        CollectionCount: u32;407        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;408409        // Basic collections410        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;411        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;412        pub WhiteList get(fn white_list): map hasher(identity) CollectionId => Vec<T::AccountId>;413414        /// Balance owner per collection map415        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;416417        /// second parameter: item id + owner account id418        pub ApprovedList get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;419420        /// Item collections421        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;422        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => FungibleItemType<T::AccountId>;423        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;424425        /// Index list426        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;427428        /// Tokens transfer baskets429        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;430        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Vec<BasketItem<T::AccountId, T::BlockNumber>>;431        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;432433        // Contract Sponsorship and Ownership434        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;435        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;436        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;437        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;438    }439    add_extra_genesis {440        build(|config: &GenesisConfig<T>| {441            // Modification of storage442            for (_num, _c) in &config.collection {443                <Module<T>>::init_collection(_c);444            }445446            for (_num, _c, _i) in &config.nft_item_id {447                <Module<T>>::init_nft_token(*_c, _i);448            }449450            for (_num, _c, _i) in &config.fungible_item_id {451                <Module<T>>::init_fungible_token(*_c, _i);452            }453454            for (_num, _c, _i) in &config.refungible_item_id {455                <Module<T>>::init_refungible_token(*_c, _i);456            }457        })458    }459}460461decl_event!(462    pub enum Event<T>463    where464        AccountId = <T as system::Trait>::AccountId,465    {466        /// New collection was created467        /// 468        /// # Arguments469        /// 470        /// * collection_id: Globally unique identifier of newly created collection.471        /// 472        /// * mode: [CollectionMode] converted into u8.473        /// 474        /// * account_id: Collection owner.475        Created(CollectionId, u8, AccountId),476477        /// New item was created.478        /// 479        /// # Arguments480        /// 481        /// * collection_id: Id of the collection where item was created.482        /// 483        /// * item_id: Id of an item. Unique within the collection.484        ItemCreated(CollectionId, TokenId),485486        /// Collection item was burned.487        /// 488        /// # Arguments489        /// 490        /// collection_id.491        /// 492        /// item_id: Identifier of burned NFT.493        ItemDestroyed(CollectionId, TokenId),494    }495);496497decl_module! {498    pub struct Module<T: Trait> for enum Call where origin: T::Origin {499500        fn deposit_event() = default;501        type Error = Error<T>;502503        fn on_initialize(now: T::BlockNumber) -> Weight {504505            if ChainVersion::get() < 2506            {507                let value = NextCollectionID::get();508                CreatedCollectionCount::put(value);509                ChainVersion::put(2);510            }511512            0513        }514515        /// 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.516        /// 517        /// # Permissions518        /// 519        /// * Anyone.520        /// 521        /// # Arguments522        /// 523        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.524        /// 525        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.526        /// 527        /// * token_prefix: UTF-8 string with token prefix.528        /// 529        /// * mode: [CollectionMode] collection type and type dependent data.530        // returns collection ID531        #[weight = T::WeightInfo::create_collection()]532        pub fn create_collection(origin,533                                 collection_name: Vec<u16>,534                                 collection_description: Vec<u16>,535                                 token_prefix: Vec<u8>,536                                 mode: CollectionMode) -> DispatchResult {537538            // Anyone can create a collection539            let who = ensure_signed(origin)?;540541            let decimal_points = match mode {542                CollectionMode::Fungible(points) => points,543                CollectionMode::ReFungible(points) => points,544                _ => 0545            };546547            // bound Total number of collections548            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);549550            // check params551            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);552553            let mut name = collection_name.to_vec();554            name.push(0);555            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);556557            let mut description = collection_description.to_vec();558            description.push(0);559            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);560561            let mut prefix = token_prefix.to_vec();562            prefix.push(0);563            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);564565            // Generate next collection ID566            let next_id = CreatedCollectionCount::get()567                .checked_add(1)568                .ok_or(Error::<T>::NumOverflow)?;569570            // bound counter571            let total = CollectionCount::get()572                .checked_add(1)573                .ok_or(Error::<T>::NumOverflow)?;574575            CreatedCollectionCount::put(next_id);576            CollectionCount::put(total);577578            // Create new collection579            let new_collection = CollectionType {580                owner: who.clone(),581                name: name,582                mode: mode.clone(),583                mint_mode: false,584                access: AccessMode::Normal,585                description: description,586                decimal_points: decimal_points,587                token_prefix: prefix,588                offchain_schema: Vec::new(),589                schema_version: SchemaVersion::ImageURL,590                sponsor: T::AccountId::default(),591                sponsor_confirmed: false,592                variable_on_chain_schema: Vec::new(),593                const_on_chain_schema: Vec::new(),594                limits: CollectionLimits::default(),595            };596597            // Add new collection to map598            <Collection<T>>::insert(next_id, new_collection);599600            // call event601            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));602603            Ok(())604        }605606        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.607        /// 608        /// # Permissions609        /// 610        /// * Collection Owner.611        /// 612        /// # Arguments613        /// 614        /// * collection_id: collection to destroy.615        #[weight = T::WeightInfo::destroy_collection()]616        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {617618            let sender = ensure_signed(origin)?;619            Self::check_owner_permissions(collection_id, sender)?;620621            <AddressTokens<T>>::remove_prefix(collection_id);622            <ApprovedList<T>>::remove_prefix(collection_id);623            <Balance<T>>::remove_prefix(collection_id);624            <ItemListIndex>::remove(collection_id);625            <AdminList<T>>::remove(collection_id);626            <Collection<T>>::remove(collection_id);627            <WhiteList<T>>::remove(collection_id);628629            <NftItemList<T>>::remove_prefix(collection_id);630            <FungibleItemList<T>>::remove_prefix(collection_id);631            <ReFungibleItemList<T>>::remove_prefix(collection_id);632633            <NftTransferBasket<T>>::remove_prefix(collection_id);634            <FungibleTransferBasket<T>>::remove_prefix(collection_id);635            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);636637            if CollectionCount::get() > 0638            {639                // bound couter640                let total = CollectionCount::get()641                    .checked_sub(1)642                    .ok_or(Error::<T>::NumOverflow)?;643644                CollectionCount::put(total);645            }646647            Ok(())648        }649650        /// Add an address to white list.651        /// 652        /// # Permissions653        /// 654        /// * Collection Owner655        /// * Collection Admin656        /// 657        /// # Arguments658        /// 659        /// * collection_id.660        /// 661        /// * address.662        #[weight = T::WeightInfo::add_to_white_list()]663        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{664665            let sender = ensure_signed(origin)?;666            Self::check_owner_or_admin_permissions(collection_id, sender)?;667668            let mut white_list_collection: Vec<T::AccountId>;669            if <WhiteList<T>>::contains_key(collection_id) {670                white_list_collection = <WhiteList<T>>::get(collection_id);671                if !white_list_collection.contains(&address.clone())672                {673                    white_list_collection.push(address.clone());674                }675            }676            else {677                white_list_collection = Vec::new();678                white_list_collection.push(address.clone());679            }680681            <WhiteList<T>>::insert(collection_id, white_list_collection);682            Ok(())683        }684685        /// Remove an address from white list.686        /// 687        /// # Permissions688        /// 689        /// * Collection Owner690        /// * Collection Admin691        /// 692        /// # Arguments693        /// 694        /// * collection_id.695        /// 696        /// * address.697        #[weight = T::WeightInfo::remove_from_white_list()]698        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{699700            let sender = ensure_signed(origin)?;701            Self::check_owner_or_admin_permissions(collection_id, sender)?;702703            if <WhiteList<T>>::contains_key(collection_id) {704                let mut white_list_collection = <WhiteList<T>>::get(collection_id);705                if white_list_collection.contains(&address.clone())706                {707                    white_list_collection.retain(|i| *i != address.clone());708                    <WhiteList<T>>::insert(collection_id, white_list_collection);709                }710            }711712            Ok(())713        }714715        /// Toggle between normal and white list access for the methods with access for `Anyone`.716        /// 717        /// # Permissions718        /// 719        /// * Collection Owner.720        /// 721        /// # Arguments722        /// 723        /// * collection_id.724        /// 725        /// * mode: [AccessMode]726        #[weight = T::WeightInfo::set_public_access_mode()]727        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult728        {729            let sender = ensure_signed(origin)?;730731            Self::check_owner_permissions(collection_id, sender)?;732            let mut target_collection = <Collection<T>>::get(collection_id);733            target_collection.access = mode;734            <Collection<T>>::insert(collection_id, target_collection);735736            Ok(())737        }738739        /// Allows Anyone to create tokens if:740        /// * White List is enabled, and741        /// * Address is added to white list, and742        /// * This method was called with True parameter743        /// 744        /// # Permissions745        /// * Collection Owner746        ///747        /// # Arguments748        /// 749        /// * collection_id.750        /// 751        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.752        #[weight = T::WeightInfo::set_mint_permission()]753        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult754        {755            let sender = ensure_signed(origin)?;756757            Self::check_owner_permissions(collection_id, sender)?;758            let mut target_collection = <Collection<T>>::get(collection_id);759            target_collection.mint_mode = mint_permission;760            <Collection<T>>::insert(collection_id, target_collection);761762            Ok(())763        }764765        /// Change the owner of the collection.766        /// 767        /// # Permissions768        /// 769        /// * Collection Owner.770        /// 771        /// # Arguments772        /// 773        /// * collection_id.774        /// 775        /// * new_owner.776        #[weight = T::WeightInfo::change_collection_owner()]777        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {778779            let sender = ensure_signed(origin)?;780            Self::check_owner_permissions(collection_id, sender)?;781            let mut target_collection = <Collection<T>>::get(collection_id);782            target_collection.owner = new_owner;783            <Collection<T>>::insert(collection_id, target_collection);784785            Ok(())786        }787788        /// Adds an admin of the Collection.789        /// 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. 790        /// 791        /// # Permissions792        /// 793        /// * Collection Owner.794        /// * Collection Admin.795        /// 796        /// # Arguments797        /// 798        /// * collection_id: ID of the Collection to add admin for.799        /// 800        /// * new_admin_id: Address of new admin to add.801        #[weight = T::WeightInfo::add_collection_admin()]802        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {803804            let sender = ensure_signed(origin)?;805            Self::check_owner_or_admin_permissions(collection_id, sender)?;806            let mut admin_arr: Vec<T::AccountId> = Vec::new();807808            if <AdminList<T>>::contains_key(collection_id)809            {810                admin_arr = <AdminList<T>>::get(collection_id);811                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);812            }813814            // Number of collection admins815            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);816817            admin_arr.push(new_admin_id);818            <AdminList<T>>::insert(collection_id, admin_arr);819820            Ok(())821        }822823        /// 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.824        ///825        /// # Permissions826        /// 827        /// * Collection Owner.828        /// * Collection Admin.829        /// 830        /// # Arguments831        /// 832        /// * collection_id: ID of the Collection to remove admin for.833        /// 834        /// * account_id: Address of admin to remove.835        #[weight = T::WeightInfo::remove_collection_admin()]836        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {837838            let sender = ensure_signed(origin)?;839            Self::check_owner_or_admin_permissions(collection_id, sender)?;840841            if <AdminList<T>>::contains_key(collection_id)842            {843                let mut admin_arr = <AdminList<T>>::get(collection_id);844                admin_arr.retain(|i| *i != account_id);845                <AdminList<T>>::insert(collection_id, admin_arr);846            }847848            Ok(())849        }850851        /// # Permissions852        /// 853        /// * Collection Owner854        /// 855        /// # Arguments856        /// 857        /// * collection_id.858        /// 859        /// * new_sponsor.860        #[weight = T::WeightInfo::set_collection_sponsor()]861        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {862863            let sender = ensure_signed(origin)?;864            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);865866            let mut target_collection = <Collection<T>>::get(collection_id);867            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);868869            target_collection.sponsor = new_sponsor;870            target_collection.sponsor_confirmed = false;871            <Collection<T>>::insert(collection_id, target_collection);872873            Ok(())874        }875876        /// # Permissions877        /// 878        /// * Sponsor.879        /// 880        /// # Arguments881        /// 882        /// * collection_id.883        #[weight = T::WeightInfo::confirm_sponsorship()]884        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {885886            let sender = ensure_signed(origin)?;887            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);888889            let mut target_collection = <Collection<T>>::get(collection_id);890            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);891892            target_collection.sponsor_confirmed = true;893            <Collection<T>>::insert(collection_id, target_collection);894895            Ok(())896        }897898        /// Switch back to pay-per-own-transaction model.899        ///900        /// # Permissions901        ///902        /// * Collection owner.903        /// 904        /// # Arguments905        /// 906        /// * collection_id.907        #[weight = T::WeightInfo::remove_collection_sponsor()]908        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {909910            let sender = ensure_signed(origin)?;911            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);912913            let mut target_collection = <Collection<T>>::get(collection_id);914            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);915916            target_collection.sponsor = T::AccountId::default();917            target_collection.sponsor_confirmed = false;918            <Collection<T>>::insert(collection_id, target_collection);919920            Ok(())921        }922923        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.924        /// 925        /// # Permissions926        /// 927        /// * Collection Owner.928        /// * Collection Admin.929        /// * Anyone if930        ///     * White List is enabled, and931        ///     * Address is added to white list, and932        ///     * MintPermission is enabled (see SetMintPermission method)933        /// 934        /// # Arguments935        /// 936        /// * collection_id: ID of the collection.937        /// 938        /// * owner: Address, initial owner of the NFT.939        ///940        /// * data: Token data to store on chain.941        // #[weight =942        // (130_000_000 as Weight)943        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))944        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))945        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]946947        #[weight = T::WeightInfo::create_item(data.len())]948        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {949950            let sender = ensure_signed(origin)?;951952            Self::collection_exists(collection_id)?;953954            let target_collection = <Collection<T>>::get(collection_id);955956            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;957            Self::validate_create_item_args(&target_collection, &data)?;958            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;959960            Ok(())961        }962963        /// This method creates multiple instances of NFT Collection created with CreateCollection method.964        /// 965        /// # Permissions966        /// 967        /// * Collection Owner.968        /// * Collection Admin.969        /// * Anyone if970        ///     * White List is enabled, and971        ///     * Address is added to white list, and972        ///     * MintPermission is enabled (see SetMintPermission method)973        /// 974        /// # Arguments975        /// 976        /// * collection_id: ID of the collection.977        /// 978        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].979        /// 980        /// * owner: Address, initial owner of the NFT.981        #[weight = T::WeightInfo::create_item(items_data.into_iter()982                               .map(|data| { data.len() })983                               .sum())]984        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {985986            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);987            let sender = ensure_signed(origin)?;988989            Self::collection_exists(collection_id)?;990            let target_collection = <Collection<T>>::get(collection_id);991992            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;993994            for data in &items_data {995                Self::validate_create_item_args(&target_collection, data)?;996            }997            for data in &items_data {998                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;999            }10001001            Ok(())1002        }10031004        /// Destroys a concrete instance of NFT.1005        /// 1006        /// # Permissions1007        /// 1008        /// * Collection Owner.1009        /// * Collection Admin.1010        /// * Current NFT Owner.1011        /// 1012        /// # Arguments1013        /// 1014        /// * collection_id: ID of the collection.1015        /// 1016        /// * item_id: ID of NFT to burn.1017        #[weight = T::WeightInfo::burn_item()]1018        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {10191020            let sender = ensure_signed(origin)?;1021            Self::collection_exists(collection_id)?;10221023            // Transfer permissions check1024            let target_collection = <Collection<T>>::get(collection_id);1025            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1026                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1027                Error::<T>::NoPermission);10281029            if target_collection.access == AccessMode::WhiteList {1030                Self::check_white_list(collection_id, &sender)?;1031            }10321033            match target_collection.mode1034            {1035                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1036                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,1037                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1038                _ => ()1039            };10401041            // call event1042            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10431044            Ok(())1045        }10461047        /// Change ownership of the token.1048        /// 1049        /// # Permissions1050        /// 1051        /// * Collection Owner1052        /// * Collection Admin1053        /// * Current NFT owner1054        ///1055        /// # Arguments1056        /// 1057        /// * recipient: Address of token recipient.1058        /// 1059        /// * collection_id.1060        /// 1061        /// * item_id: ID of the item1062        ///     * Non-Fungible Mode: Required.1063        ///     * Fungible Mode: Ignored.1064        ///     * Re-Fungible Mode: Required.1065        /// 1066        /// * value: Amount to transfer.1067        ///     * Non-Fungible Mode: Ignored1068        ///     * Fungible Mode: Must specify transferred amount1069        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1070        #[weight = T::WeightInfo::transfer()]1071        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10721073            let sender = ensure_signed(origin)?;1074            let target_collection = <Collection<T>>::get(collection_id);10751076            // Limits check1077            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;10781079            // Transfer permissions check1080            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1081                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1082                Error::<T>::NoPermission);10831084            if target_collection.access == AccessMode::WhiteList {1085                Self::check_white_list(collection_id, &sender)?;1086                Self::check_white_list(collection_id, &recipient)?;1087            }10881089            match target_collection.mode1090            {1091                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1092                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1093                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1094                _ => ()1095            };10961097            Ok(())1098        }10991100        /// Set, change, or remove approved address to transfer the ownership of the NFT.1101        /// 1102        /// # Permissions1103        /// 1104        /// * Collection Owner1105        /// * Collection Admin1106        /// * Current NFT owner1107        /// 1108        /// # Arguments1109        /// 1110        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1111        /// 1112        /// * collection_id.1113        /// 1114        /// * item_id: ID of the item.1115        #[weight = T::WeightInfo::approve()]1116        pub fn approve(origin, approved: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {11171118            let sender = ensure_signed(origin)?;11191120            // Transfer permissions check1121            let target_collection = <Collection<T>>::get(collection_id);1122            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1123                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1124                Error::<T>::NoPermission);11251126            if target_collection.access == AccessMode::WhiteList {1127                Self::check_white_list(collection_id, &sender)?;1128                Self::check_white_list(collection_id, &approved)?;1129            }11301131            // amount param stub1132            let amount = 100000000;11331134            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1135            if list_exists {11361137                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1138                let item_contains = list.iter().any(|i| i.approved == approved);11391140                if !item_contains {1141                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1142                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1143                }1144            } else {11451146                let mut list = Vec::new();1147                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1148                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1149            }11501151            Ok(())1152        }1153        1154        /// 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.1155        /// 1156        /// # Permissions1157        /// * Collection Owner1158        /// * Collection Admin1159        /// * Current NFT owner1160        /// * Address approved by current NFT owner1161        /// 1162        /// # Arguments1163        /// 1164        /// * from: Address that owns token.1165        /// 1166        /// * recipient: Address of token recipient.1167        /// 1168        /// * collection_id.1169        /// 1170        /// * item_id: ID of the item.1171        /// 1172        /// * value: Amount to transfer.1173        #[weight = T::WeightInfo::transfer_from()]1174        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11751176            let sender = ensure_signed(origin)?;1177            let mut appoved_transfer = false;11781179            // Check approve1180            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1181                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1182                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1183                if opt_item.is_some()1184                {1185                    appoved_transfer = true;1186                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1187                }1188            }11891190            let target_collection = <Collection<T>>::get(collection_id);11911192            // Limits check1193            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11941195            // Transfer permissions check         1196            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1197            Error::<T>::NoPermission);11981199            if target_collection.access == AccessMode::WhiteList {1200                Self::check_white_list(collection_id, &sender)?;1201                Self::check_white_list(collection_id, &recipient)?;1202            }12031204            // remove approve1205            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1206                .into_iter().filter(|i| i.approved != sender.clone()).collect();1207            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);120812091210            match target_collection.mode1211            {1212                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1213                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1214                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1215                _ => ()1216            };12171218            Ok(())1219        }12201221        #[weight = 0]1222        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {12231224            // let no_perm_mes = "You do not have permissions to modify this collection";1225            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1226            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1227            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);12281229            // // on_nft_received  call12301231            // Self::transfer(origin, collection_id, item_id, new_owner)?;12321233            Ok(())1234        }12351236        /// Set off-chain data schema.1237        /// 1238        /// # Permissions1239        /// 1240        /// * Collection Owner1241        /// * Collection Admin1242        /// 1243        /// # Arguments1244        /// 1245        /// * collection_id.1246        /// 1247        /// * schema: String representing the offchain data schema.1248        #[weight = T::WeightInfo::set_variable_meta_data()]1249        pub fn set_variable_meta_data (1250            origin,1251            collection_id: CollectionId,1252            item_id: TokenId,1253            data: Vec<u8>1254        ) -> DispatchResult {1255            let sender = ensure_signed(origin)?;1256            1257            Self::collection_exists(collection_id)?;1258            1259            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12601261            // Modify permissions check1262            let target_collection = <Collection<T>>::get(collection_id);1263            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1264                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1265                Error::<T>::NoPermission);12661267            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12681269            match target_collection.mode1270            {1271                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1272                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1273                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1274                _ => fail!(Error::<T>::UnexpectedCollectionType)1275            };12761277            Ok(())1278        }1279 1280        /// Set schema standard1281        /// ImageURL1282        /// Unique1283        /// 1284        /// # Permissions1285        /// 1286        /// * Collection Owner1287        /// * Collection Admin1288        /// 1289        /// # Arguments1290        /// 1291        /// * collection_id.1292        /// 1293        /// * schema: SchemaVersion: enum1294        #[weight = 0]1295        pub fn set_schema_version(1296            origin,1297            collection_id: CollectionId,1298            version: SchemaVersion1299        ) -> DispatchResult {1300            let sender = ensure_signed(origin)?;1301            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1302            let mut target_collection = <Collection<T>>::get(collection_id);1303            target_collection.schema_version = version;1304            <Collection<T>>::insert(collection_id, target_collection);13051306            Ok(())1307        }13081309        /// Set off-chain data schema.1310        /// 1311        /// # Permissions1312        /// 1313        /// * Collection Owner1314        /// * Collection Admin1315        /// 1316        /// # Arguments1317        /// 1318        /// * collection_id.1319        /// 1320        /// * schema: String representing the offchain data schema.1321        #[weight = T::WeightInfo::set_offchain_schema()]1322        pub fn set_offchain_schema(1323            origin,1324            collection_id: CollectionId,1325            schema: Vec<u8>1326        ) -> DispatchResult {1327            let sender = ensure_signed(origin)?;1328            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13291330            let mut target_collection = <Collection<T>>::get(collection_id);1331            target_collection.offchain_schema = schema;1332            <Collection<T>>::insert(collection_id, target_collection);13331334            Ok(())1335        }13361337        /// Set const on-chain data schema.1338        /// 1339        /// # Permissions1340        /// 1341        /// * Collection Owner1342        /// * Collection Admin1343        /// 1344        /// # Arguments1345        /// 1346        /// * collection_id.1347        /// 1348        /// * schema: String representing the const on-chain data schema.1349        #[weight = T::WeightInfo::set_const_on_chain_schema()]1350        pub fn set_const_on_chain_schema (1351            origin,1352            collection_id: CollectionId,1353            schema: Vec<u8>1354        ) -> DispatchResult {1355            let sender = ensure_signed(origin)?;1356            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13571358            let mut target_collection = <Collection<T>>::get(collection_id);1359            target_collection.const_on_chain_schema = schema;1360            <Collection<T>>::insert(collection_id, target_collection);13611362            Ok(())1363        }13641365        /// Set variable on-chain data schema.1366        /// 1367        /// # Permissions1368        /// 1369        /// * Collection Owner1370        /// * Collection Admin1371        /// 1372        /// # Arguments1373        /// 1374        /// * collection_id.1375        /// 1376        /// * schema: String representing the variable on-chain data schema.1377        #[weight = T::WeightInfo::set_const_on_chain_schema()]1378        pub fn set_variable_on_chain_schema (1379            origin,1380            collection_id: CollectionId,1381            schema: Vec<u8>1382        ) -> DispatchResult {1383            let sender = ensure_signed(origin)?;1384            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13851386            let mut target_collection = <Collection<T>>::get(collection_id);1387            target_collection.variable_on_chain_schema = schema;1388            <Collection<T>>::insert(collection_id, target_collection);13891390            Ok(())1391        }13921393        // Sudo permissions function1394        #[weight = 0]1395        pub fn set_chain_limits(1396            origin,1397            limits: ChainLimits1398        ) -> DispatchResult {1399            ensure_root(origin)?;1400            <ChainLimit>::put(limits);1401            Ok(())1402        }14031404        /// Enable smart contract self-sponsoring.1405        /// 1406        /// # Permissions1407        /// 1408        /// * Contract Owner1409        /// 1410        /// # Arguments1411        /// 1412        /// * contract address1413        /// * enable flag1414        /// 1415        #[weight = T::WeightInfo::enable_contract_sponsoring()]1416        pub fn enable_contract_sponsoring(1417            origin,1418            contract_address: T::AccountId,1419            enable: bool1420        ) -> DispatchResult {14211422            let sender = ensure_signed(origin)?;14231424            #[cfg(feature = "runtime-benchmarks")]1425            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14261427            let mut is_owner = false;1428            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1429                let owner = <ContractOwner<T>>::get(&contract_address);1430                is_owner = sender == owner;1431            }1432            ensure!(is_owner, Error::<T>::NoPermission);14331434            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1435            Ok(())1436        }14371438        /// Set the rate limit for contract sponsoring to specified number of blocks.1439        /// 1440        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1441        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1442        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1443        /// from contract endowment if there are at least B blocks between such transactions. 1444        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1445        /// 1446        /// # Permissions1447        /// 1448        /// * Contract Owner1449        /// 1450        /// # Arguments1451        /// 1452        /// -`contract_address`: Address of the contract to sponsor1453        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1454        /// 1455        #[weight = 0]1456        pub fn set_contract_sponsoring_rate_limit(1457            origin,1458            contract_address: T::AccountId,1459            rate_limit: T::BlockNumber1460        ) -> DispatchResult {1461            let sender = ensure_signed(origin)?;1462            let mut is_owner = false;1463            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1464                let owner = <ContractOwner<T>>::get(&contract_address);1465                is_owner = sender == owner;1466            }1467            ensure!(is_owner, Error::<T>::NoPermission);14681469            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1470            Ok(())1471        }14721473        #[weight = 0]1474        pub fn set_collection_limits(1475            origin,1476            collection_id: u32,1477            limits: CollectionLimits,1478        ) -> DispatchResult {1479            let sender = ensure_signed(origin)?;1480            Self::check_owner_permissions(collection_id, sender.clone())?;1481            let mut target_collection = <Collection<T>>::get(collection_id);1482            let chain_limits = ChainLimit::get();1483            let climits = target_collection.limits;14841485            // collection bounds1486            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1487                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1488                Error::<T>::CollectionLimitBoundsExceeded);14891490            // token_limit   check  prev1491            ensure!(climits.token_limit > limits.token_limit && 1492                limits.token_limit <= chain_limits.account_token_ownership_limit, 1493                Error::<T>::AccountTokenLimitExceeded);14941495            target_collection.limits = limits;1496            <Collection<T>>::insert(collection_id, target_collection);14971498            Ok(())1499        } 1500    }1501}15021503impl<T: Trait> Module<T> {15041505    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15061507        // check token limit and account token limit1508        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1509        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1510        1511        Ok(())1512    }15131514    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15151516        // check token limit and account token limit1517        let total_items: u32 = ItemListIndex::get(collection_id);1518        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1519        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1520        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15211522        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1523            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1524            Self::check_white_list(collection_id, owner)?;1525            Self::check_white_list(collection_id, sender)?;1526        }15271528        Ok(())1529    }15301531    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1532        match target_collection.mode1533        {1534            CollectionMode::NFT => {1535                if let CreateItemData::NFT(data) = data {1536                    // check sizes1537                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1538                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1539                } else {1540                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1541                }1542            },1543            CollectionMode::Fungible(_) => {1544                if let CreateItemData::Fungible(_) = data {1545                } else {1546                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1547                }1548            },1549            CollectionMode::ReFungible(_) => {1550                if let CreateItemData::ReFungible(data) = data {15511552                    // check sizes1553                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1554                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1555                } else {1556                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1557                }1558            },1559            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1560        };15611562        Ok(())1563    }15641565    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1566        match data1567        {1568            CreateItemData::NFT(data) => {1569                let item = NftItemType {1570                    owner,1571                    const_data: data.const_data,1572                    variable_data: data.variable_data1573                };15741575                Self::add_nft_item(collection_id, item)?;1576            },1577            CreateItemData::Fungible(_) => {1578                let item = FungibleItemType {1579                    owner,1580                    value: (10 as u128).pow(collection.decimal_points as u32)1581                };15821583                Self::add_fungible_item(collection_id, item)?;1584            },1585            CreateItemData::ReFungible(data) => {1586                let mut owner_list = Vec::new();1587                let value = (10 as u128).pow(collection.decimal_points as u32);1588                owner_list.push(Ownership {owner: owner.clone(), fraction: value});15891590                let item = ReFungibleItemType {1591                    owner: owner_list,1592                    const_data: data.const_data,1593                    variable_data: data.variable_data1594                };15951596                Self::add_refungible_item(collection_id, item)?;1597            }1598        };15991600        // call event1601        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16021603        Ok(())1604    }16051606    fn add_fungible_item(collection_id: CollectionId, item: FungibleItemType<T::AccountId>) -> DispatchResult {1607        let current_index = <ItemListIndex>::get(collection_id)1608            .checked_add(1)1609            .ok_or(Error::<T>::NumOverflow)?;1610        let itemcopy = item.clone();1611        let owner = item.owner.clone();16121613        Self::add_token_index(collection_id, current_index, owner.clone())?;16141615        <ItemListIndex>::insert(collection_id, current_index);1616        <FungibleItemList<T>>::insert(collection_id, current_index, itemcopy);16171618        // Add current block1619        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1620        <FungibleTransferBasket<T>>::insert(collection_id, current_index, v);1621        1622        // Update balance1623        let new_balance = <Balance<T>>::get(collection_id, owner.clone())1624            .checked_add(item.value)1625            .ok_or(Error::<T>::NumOverflow)?;1626        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);16271628        Ok(())1629    }16301631    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1632        let current_index = <ItemListIndex>::get(collection_id)1633            .checked_add(1)1634            .ok_or(Error::<T>::NumOverflow)?;1635        let itemcopy = item.clone();16361637        let value = item.owner.first().unwrap().fraction;1638        let owner = item.owner.first().unwrap().owner.clone();16391640        Self::add_token_index(collection_id, current_index, owner.clone())?;16411642        <ItemListIndex>::insert(collection_id, current_index);1643        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);16441645        // Add current block1646        let block_number: T::BlockNumber = 0.into();1647        <ReFungibleTransferBasket<T>>::insert(collection_id, current_index, block_number);16481649        // Update balance1650        let new_balance = <Balance<T>>::get(collection_id, owner.clone())1651            .checked_add(value)1652            .ok_or(Error::<T>::NumOverflow)?;1653        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);16541655        Ok(())1656    }16571658    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1659        let current_index = <ItemListIndex>::get(collection_id)1660            .checked_add(1)1661            .ok_or(Error::<T>::NumOverflow)?;16621663        let item_owner = item.owner.clone();1664        Self::add_token_index(collection_id, current_index, item.owner.clone())?;16651666        <ItemListIndex>::insert(collection_id, current_index);1667        <NftItemList<T>>::insert(collection_id, current_index, item);16681669        // Add current block1670        let block_number: T::BlockNumber = 0.into();1671        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);16721673        // Update balance1674        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1675            .checked_add(1)1676            .ok_or(Error::<T>::NumOverflow)?;1677        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);16781679        Ok(())1680    }16811682    fn burn_refungible_item(1683        collection_id: CollectionId,1684        item_id: TokenId,1685        owner: T::AccountId,1686    ) -> DispatchResult {1687        ensure!(1688            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1689            Error::<T>::TokenNotFound1690        );1691        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1692        let item = collection1693            .owner1694            .iter()1695            .filter(|&i| i.owner == owner)1696            .next()1697            .unwrap();1698        Self::remove_token_index(collection_id, item_id, owner.clone())?;16991700        // remove approve list1701        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));17021703        // update balance1704        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1705            .checked_sub(item.fraction)1706            .ok_or(Error::<T>::NumOverflow)?;1707        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17081709        <ReFungibleItemList<T>>::remove(collection_id, item_id);17101711        Ok(())1712    }17131714    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1715        ensure!(1716            <NftItemList<T>>::contains_key(collection_id, item_id),1717            Error::<T>::TokenNotFound1718        );1719        let item = <NftItemList<T>>::get(collection_id, item_id);1720        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17211722        // remove approve list1723        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));17241725        // update balance1726        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1727            .checked_sub(1)1728            .ok_or(Error::<T>::NumOverflow)?;1729        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1730        <NftItemList<T>>::remove(collection_id, item_id);17311732        Ok(())1733    }17341735    fn burn_fungible_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1736        ensure!(1737            <FungibleItemList<T>>::contains_key(collection_id, item_id),1738            Error::<T>::TokenNotFound1739        );1740        let item = <FungibleItemList<T>>::get(collection_id, item_id);1741        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17421743        // remove approve list1744        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));17451746        // update balance1747        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1748            .checked_sub(item.value)1749            .ok_or(Error::<T>::NumOverflow)?;1750        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17511752        <FungibleItemList<T>>::remove(collection_id, item_id);17531754        Ok(())1755    }17561757    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1758        ensure!(1759            <Collection<T>>::contains_key(collection_id),1760            Error::<T>::CollectionNotFound1761        );1762        Ok(())1763    }17641765    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1766        Self::collection_exists(collection_id)?;17671768        let target_collection = <Collection<T>>::get(collection_id);1769        ensure!(1770            subject == target_collection.owner,1771            Error::<T>::NoPermission1772        );17731774        Ok(())1775    }17761777    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1778        let target_collection = <Collection<T>>::get(collection_id);1779        let mut result: bool = subject == target_collection.owner;1780        let exists = <AdminList<T>>::contains_key(collection_id);17811782        if !result & exists {1783            if <AdminList<T>>::get(collection_id).contains(&subject) {1784                result = true1785            }1786        }17871788        result1789    }17901791    fn check_owner_or_admin_permissions(1792        collection_id: CollectionId,1793        subject: T::AccountId,1794    ) -> DispatchResult {1795        Self::collection_exists(collection_id)?;1796        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());17971798        ensure!(1799            result,1800            Error::<T>::NoPermission1801        );1802        Ok(())1803    }18041805    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1806        let target_collection = <Collection<T>>::get(collection_id);18071808        match target_collection.mode {1809            CollectionMode::NFT => {1810                <NftItemList<T>>::get(collection_id, item_id).owner == subject1811            }1812            CollectionMode::Fungible(_) => {1813                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1814            }1815            CollectionMode::ReFungible(_) => {1816                <ReFungibleItemList<T>>::get(collection_id, item_id)1817                    .owner1818                    .iter()1819                    .any(|i| i.owner == subject)1820            }1821            CollectionMode::Invalid => false,1822        }1823    }18241825    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1826        let mes = Error::<T>::AddresNotInWhiteList;1827        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1828        let wl = <WhiteList<T>>::get(collection_id);1829        ensure!(wl.contains(address), mes);18301831        Ok(())1832    }18331834    fn transfer_fungible(1835        collection_id: CollectionId,1836        item_id: TokenId,1837        value: u128,1838        owner: T::AccountId,1839        new_owner: T::AccountId,1840    ) -> DispatchResult {1841        ensure!(1842            <FungibleItemList<T>>::contains_key(collection_id, item_id),1843            Error::<T>::TokenNotFound1844        );18451846        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1847        let amount = full_item.value;18481849        ensure!(amount >= value, Error::<T>::TokenValueTooLow);18501851        // update balance1852        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1853            .checked_sub(value)1854            .ok_or(Error::<T>::NumOverflow)?;1855        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);18561857        let mut new_owner_account_id = 0;1858        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1859        if new_owner_items.len() > 0 {1860            new_owner_account_id = new_owner_items[0];1861        }18621863        // transfer1864        if amount == value && new_owner_account_id == 0 {1865            // change owner1866            // new owner do not have account1867            let mut new_full_item = full_item.clone();1868            new_full_item.owner = new_owner.clone();1869            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18701871            // update balance1872            let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1873                .checked_add(value)1874                .ok_or(Error::<T>::NumOverflow)?;1875            <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);18761877            // update index collection1878            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1879        } else {1880            let mut new_full_item = full_item.clone();1881            new_full_item.value -= value;18821883            // separate amount1884            if new_owner_account_id > 0 {1885                // new owner has account1886                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1887                item.value += value;18881889                // update balance1890                let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1891                    .checked_add(value)1892                    .ok_or(Error::<T>::NumOverflow)?;1893                <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);18941895                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1896            } else {1897                // new owner do not have account1898                let item = FungibleItemType {1899                    owner: new_owner.clone(),1900                    value1901                };19021903                Self::add_fungible_item(collection_id, item)?;1904            }19051906            if amount == value {1907                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;19081909                // remove approve list1910                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1911                <FungibleItemList<T>>::remove(collection_id, item_id);1912            }19131914            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1915        }19161917        Ok(())1918    }19191920    fn transfer_refungible(1921        collection_id: CollectionId,1922        item_id: TokenId,1923        value: u128,1924        owner: T::AccountId,1925        new_owner: T::AccountId,1926    ) -> DispatchResult {1927        ensure!(1928            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1929            Error::<T>::TokenNotFound1930        );19311932        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1933        let item = full_item1934            .owner1935            .iter()1936            .filter(|i| i.owner == owner)1937            .next()1938            .ok_or(Error::<T>::NumOverflow)?;1939        let amount = item.fraction;19401941        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19421943        // update balance1944        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1945            .checked_sub(value)1946            .ok_or(Error::<T>::NumOverflow)?;1947        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19481949        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1950            .checked_add(value)1951            .ok_or(Error::<T>::NumOverflow)?;1952        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19531954        let old_owner = item.owner.clone();1955        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19561957        // transfer1958        if amount == value && !new_owner_has_account {1959            // change owner1960            // new owner do not have account1961            let mut new_full_item = full_item.clone();1962            new_full_item1963                .owner1964                .iter_mut()1965                .find(|i| i.owner == owner)1966                .unwrap()1967                .owner = new_owner.clone();1968            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19691970            // update index collection1971            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1972        } else {1973            let mut new_full_item = full_item.clone();1974            new_full_item1975                .owner1976                .iter_mut()1977                .find(|i| i.owner == owner)1978                .unwrap()1979                .fraction -= value;19801981            // separate amount1982            if new_owner_has_account {1983                // new owner has account1984                new_full_item1985                    .owner1986                    .iter_mut()1987                    .find(|i| i.owner == new_owner)1988                    .unwrap()1989                    .fraction += value;1990            } else {1991                // new owner do not have account1992                new_full_item.owner.push(Ownership {1993                    owner: new_owner.clone(),1994                    fraction: value,1995                });1996                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1997            }19981999            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2000        }20012002        Ok(())2003    }20042005    fn transfer_nft(2006        collection_id: CollectionId,2007        item_id: TokenId,2008        sender: T::AccountId,2009        new_owner: T::AccountId,2010    ) -> DispatchResult {2011        ensure!(2012            <NftItemList<T>>::contains_key(collection_id, item_id),2013            Error::<T>::TokenNotFound2014        );20152016        let mut item = <NftItemList<T>>::get(collection_id, item_id);20172018        ensure!(2019            sender == item.owner,2020            Error::<T>::MustBeTokenOwner2021        );20222023        // update balance2024        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2025            .checked_sub(1)2026            .ok_or(Error::<T>::NumOverflow)?;2027        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20282029        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2030            .checked_add(1)2031            .ok_or(Error::<T>::NumOverflow)?;2032        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20332034        // change owner2035        let old_owner = item.owner.clone();2036        item.owner = new_owner.clone();2037        <NftItemList<T>>::insert(collection_id, item_id, item);20382039        // update index collection2040        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;20412042        // reset approved list2043        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));2044        Ok(())2045    }2046    2047    fn item_exists(2048        collection_id: CollectionId,2049        item_id: TokenId,2050        mode: &CollectionMode2051    ) -> DispatchResult {2052        match mode {2053            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2054            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2055            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2056            _ => ()2057        };2058        2059        Ok(())2060    }20612062    fn set_re_fungible_variable_data(2063        collection_id: CollectionId,2064        item_id: TokenId,2065        data: Vec<u8>2066    ) -> DispatchResult {2067        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20682069        item.variable_data = data;20702071        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20722073        Ok(())2074    }20752076    fn set_nft_variable_data(2077        collection_id: CollectionId,2078        item_id: TokenId,2079        data: Vec<u8>2080    ) -> DispatchResult {2081        let mut item = <NftItemList<T>>::get(collection_id, item_id);2082        2083        item.variable_data = data;20842085        <NftItemList<T>>::insert(collection_id, item_id, item);2086        2087        Ok(())2088    }20892090    fn init_collection(item: &CollectionType<T::AccountId>) {2091        // check params2092        assert!(2093            item.decimal_points <= MAX_DECIMAL_POINTS,2094            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2095        );2096        assert!(2097            item.name.len() <= 64,2098            "Collection name can not be longer than 63 char"2099        );2100        assert!(2101            item.name.len() <= 256,2102            "Collection description can not be longer than 255 char"2103        );2104        assert!(2105            item.token_prefix.len() <= 16,2106            "Token prefix can not be longer than 15 char"2107        );21082109        // Generate next collection ID2110        let next_id = CreatedCollectionCount::get()2111            .checked_add(1)2112            .unwrap();21132114        CreatedCollectionCount::put(next_id);2115    }21162117    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2118        let current_index = <ItemListIndex>::get(collection_id)2119            .checked_add(1)2120            .unwrap();21212122        let item_owner = item.owner.clone();2123        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();21242125        <ItemListIndex>::insert(collection_id, current_index);21262127        // Update balance2128        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2129            .checked_add(1)2130            .unwrap();2131        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2132    }21332134    fn init_fungible_token(collection_id: CollectionId, item: &FungibleItemType<T::AccountId>) {2135        let current_index = <ItemListIndex>::get(collection_id)2136            .checked_add(1)2137            .unwrap();2138        let owner = item.owner.clone();21392140        Self::add_token_index(collection_id, current_index, owner.clone()).unwrap();21412142        <ItemListIndex>::insert(collection_id, current_index);21432144        // Update balance2145        let new_balance = <Balance<T>>::get(collection_id, owner.clone())2146            .checked_add(item.value)2147            .unwrap();2148        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2149    }21502151    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2152        let current_index = <ItemListIndex>::get(collection_id)2153            .checked_add(1)2154            .unwrap();21552156        let value = item.owner.first().unwrap().fraction;2157        let owner = item.owner.first().unwrap().owner.clone();21582159        Self::add_token_index(collection_id, current_index, owner.clone()).unwrap();21602161        <ItemListIndex>::insert(collection_id, current_index);21622163        // Update balance2164        let new_balance = <Balance<T>>::get(collection_id, owner.clone())2165            .checked_add(value)2166            .unwrap();2167        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2168    }21692170    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {21712172        // add to account limit2173        if <AccountItemCount<T>>::contains_key(owner.clone()) {21742175            // bound Owned tokens by a single address2176            let count = <AccountItemCount<T>>::get(owner.clone());2177            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21782179            <AccountItemCount<T>>::insert(owner.clone(), count2180                .checked_add(1)2181                .ok_or(Error::<T>::NumOverflow)?);2182        }2183        else {2184            <AccountItemCount<T>>::insert(owner.clone(), 1);2185        }21862187        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2188        if list_exists {2189            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2190            let item_contains = list.contains(&item_index.clone());21912192            if !item_contains {2193                list.push(item_index.clone());2194            }21952196            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2197        } else {2198            let mut itm = Vec::new();2199            itm.push(item_index.clone());2200            <AddressTokens<T>>::insert(collection_id, owner, itm);2201            2202        }22032204        Ok(())2205    }22062207    fn remove_token_index(2208        collection_id: CollectionId,2209        item_index: TokenId,2210        owner: T::AccountId,2211    ) -> DispatchResult {22122213        // update counter2214        <AccountItemCount<T>>::insert(owner.clone(), 2215            <AccountItemCount<T>>::get(owner.clone())2216            .checked_sub(1)2217            .ok_or(Error::<T>::NumOverflow)?);221822192220        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2221        if list_exists {2222            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2223            let item_contains = list.contains(&item_index.clone());22242225            if item_contains {2226                list.retain(|&item| item != item_index);2227                <AddressTokens<T>>::insert(collection_id, owner, list);2228            }2229        }22302231        Ok(())2232    }22332234    fn move_token_index(2235        collection_id: CollectionId,2236        item_index: TokenId,2237        old_owner: T::AccountId,2238        new_owner: T::AccountId,2239    ) -> DispatchResult {2240        Self::remove_token_index(collection_id, item_index, old_owner)?;2241        Self::add_token_index(collection_id, item_index, new_owner)?;22422243        Ok(())2244    }2245}22462247////////////////////////////////////////////////////////////////////////////////////////////////////2248// Economic models2249// #region22502251/// Fee multiplier.2252pub type Multiplier = FixedU128;22532254type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2255    <T as system::Trait>::AccountId,2256>>::Balance;2257type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2258    <T as system::Trait>::AccountId,2259>>::NegativeImbalance;22602261/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2262/// in the queue.2263#[derive(Encode, Decode, Clone, Eq, PartialEq)]2264pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2265    #[codec(compact)] BalanceOf<T>2266);22672268impl<T: Trait + Send + Sync> sp_std::fmt::Debug2269    for ChargeTransactionPayment<T>2270{2271    #[cfg(feature = "std")]2272    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2273        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2274    }2275    #[cfg(not(feature = "std"))]2276    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2277        Ok(())2278    }2279}22802281impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2282where2283    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2284    BalanceOf<T>: Send + Sync + FixedPointOperand,2285{2286    /// utility constructor. Used only in client/factory code.2287    pub fn from(fee: BalanceOf<T>) -> Self {2288        Self(fee)2289    }22902291    pub fn traditional_fee(2292        len: usize,2293        info: &DispatchInfoOf<T::Call>,2294        tip: BalanceOf<T>,2295    ) -> BalanceOf<T>2296    where2297        T::Call: Dispatchable<Info = DispatchInfo>,2298    {2299        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2300    }23012302	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2303		let weight_saturation = T::MaximumBlockWeight::get() / info.weight.max(1);2304		let len_saturation = T::MaximumBlockLength::get() as u64 / (len as u64).max(1);2305		let coefficient: BalanceOf<T> = weight_saturation.min(len_saturation).saturated_into::<BalanceOf<T>>();2306		final_fee.saturating_mul(coefficient).saturated_into::<TransactionPriority>()2307	}23082309    fn withdraw_fee(2310        &self,2311        who: &T::AccountId,2312        call: &T::Call,2313        info: &DispatchInfoOf<T::Call>,2314        len: usize,2315    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2316        let tip = self.0;23172318        // Set fee based on call type. Creating collection costs 1 Unique.2319        // All other transactions have traditional fees so far2320        // let fee = match call.is_sub_type() {2321        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2322        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2323        //                                                 // _ => <BalanceOf<T>>::from(100)2324        // };2325        let fee = Self::traditional_fee(len, info, tip);23262327        // Only mess with balances if fee is not zero.2328        if fee.is_zero() {2329            return Ok((fee, None));2330        }23312332        // Determine who is paying transaction fee based on ecnomic model2333        // Parse call to extract collection ID and access collection sponsor2334        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2335            Some(Call::create_item(collection_id, _owner, _properties)) => {23362337                // check free create limit2338                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2339                   (<Collection<T>>::get(collection_id).sponsor_confirmed)2340                {2341                    <Collection<T>>::get(collection_id).sponsor2342                } else {2343                    T::AccountId::default()2344                }2345            }2346            Some(Call::transfer(new_owner, collection_id, item_id, _value)) => {2347                2348                let mut sponsor_transfer = false;2349                if <Collection<T>>::get(collection_id).sponsor_confirmed {23502351                    let collection_limits = <Collection<T>>::get(collection_id).limits;2352                    let collection_mode = <Collection<T>>::get(collection_id).mode;2353    2354                    // sponsor timeout2355                    sponsor_transfer = match collection_mode {2356                        CollectionMode::NFT => {2357    2358                            // get correct limit2359                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2360                                collection_limits.sponsor_transfer_timeout2361                            } else {2362                                ChainLimit::get().nft_sponsor_transfer_timeout2363                            };2364    2365                            let basket = <NftTransferBasket<T>>::get(collection_id, item_id);2366                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2367                            let limit_time = basket + limit.into();2368                            if block_number >= limit_time {2369                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2370                                true2371                            }2372                            else {2373                                false2374                            }2375                        }2376                        CollectionMode::Fungible(_) => {2377    2378                            // get correct limit2379                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2380                                collection_limits.sponsor_transfer_timeout2381                            } else {2382                                ChainLimit::get().fungible_sponsor_transfer_timeout2383                            };2384    2385                            let mut basket = <FungibleTransferBasket<T>>::get(collection_id, item_id);2386                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2387                            if basket.iter().any(|i| i.address == new_owner.clone())2388                            {2389                                let item = basket.iter_mut().find(|i| i.address == new_owner.clone()).unwrap().clone();2390                                let limit_time = item.start_block + limit.into();2391                                if block_number >= limit_time {2392                                    basket.retain(|x| x.address == item.address);2393                                    basket.push(BasketItem { start_block: block_number, address: new_owner.clone() });2394                                    <FungibleTransferBasket<T>>::insert(collection_id, item_id, basket);2395                                    true2396                                }2397                                else {2398                                    false2399                                }2400                            }2401                            else {2402                                basket.push(BasketItem { start_block: block_number, address: new_owner.clone()});2403                                true2404                            }2405                        }2406                        CollectionMode::ReFungible(_) => {2407    2408                            // get correct limit2409                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2410                                collection_limits.sponsor_transfer_timeout2411                            } else {2412                                ChainLimit::get().refungible_sponsor_transfer_timeout2413                            };2414    2415                            let basket = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2416                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2417                            let limit_time = basket + limit.into();2418                            if block_number >= limit_time {2419                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2420                                true2421                            } else {2422                                false2423                            }2424                        }2425                        _ => {2426                            false2427                        },2428                    };2429                }24302431                if !sponsor_transfer {2432                    T::AccountId::default()2433                } else {2434                    <Collection<T>>::get(collection_id).sponsor2435                }2436            }24372438            _ => T::AccountId::default(),2439        };24402441        // Sponsor smart contracts2442        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {24432444            // On instantiation: set the contract owner2445            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {24462447                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2448                    code_hash,2449                    &data,2450                    &who,2451                );2452                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());24532454                T::AccountId::default()2455            },24562457            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2458            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {24592460                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());24612462                let mut sponsor_transfer = false;2463                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2464                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2465                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2466                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2467                    let limit_time = last_tx_block + rate_limit;24682469                    if block_number >= limit_time {2470                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2471                        sponsor_transfer = true;2472                    }2473                } else {2474                    sponsor_transfer = false;2475                }2476               2477                2478                let mut sp = T::AccountId::default();2479                if sponsor_transfer {2480                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2481                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2482                            sp = called_contract;2483                        }2484                    }2485                }24862487                sp2488            },24892490            _ => sponsor,2491        };24922493        let mut who_pays_fee: T::AccountId = sponsor.clone();2494        if sponsor == T::AccountId::default() {2495            who_pays_fee = who.clone();2496        }24972498        match <T as transaction_payment::Trait>::Currency::withdraw(2499            &who_pays_fee,2500            fee,2501            if tip.is_zero() {2502                WithdrawReason::TransactionPayment.into()2503            } else {2504                WithdrawReason::TransactionPayment | WithdrawReason::Tip2505            },2506            ExistenceRequirement::KeepAlive,2507        ) {2508            Ok(imbalance) => Ok((fee, Some(imbalance))),2509            Err(_) => Err(InvalidTransaction::Payment.into()),2510        }2511    }2512}251325142515impl<T: Trait + Send + Sync> SignedExtension2516    for ChargeTransactionPayment<T>2517where2518    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2519    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2520{2521    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2522    type AccountId = T::AccountId;2523    type Call = T::Call;2524    type AdditionalSigned = ();2525    type Pre = (2526        BalanceOf<T>,2527        Self::AccountId,2528        Option<NegativeImbalanceOf<T>>,2529        BalanceOf<T>,2530    );2531    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2532        Ok(())2533    }25342535    fn validate(2536        &self,2537        who: &Self::AccountId,2538        call: &Self::Call,2539        info: &DispatchInfoOf<Self::Call>,2540        len: usize,2541    ) -> TransactionValidity {2542		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2543		Ok(ValidTransaction {2544			priority: Self::get_priority(len, info, fee),2545			..Default::default()2546		})2547    }25482549    fn pre_dispatch(2550        self,2551        who: &Self::AccountId,2552        call: &Self::Call,2553        info: &DispatchInfoOf<Self::Call>,2554        len: usize,2555    ) -> Result<Self::Pre, TransactionValidityError> {2556        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2557        Ok((self.0, who.clone(), imbalance, fee))2558    }25592560    fn post_dispatch(2561        pre: Self::Pre,2562        info: &DispatchInfoOf<Self::Call>,2563        post_info: &PostDispatchInfoOf<Self::Call>,2564        len: usize,2565        _result: &DispatchResult,2566    ) -> Result<(), TransactionValidityError> {2567        let (tip, who, imbalance, fee) = pre;2568        if let Some(payed) = imbalance {2569            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2570                len as u32, info, post_info, tip,2571            );2572            let refund = fee.saturating_sub(actual_fee);2573            let actual_payment =2574                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2575                    &who, refund,2576                ) {2577                    Ok(refund_imbalance) => {2578                        // The refund cannot be larger than the up front payed max weight.2579                        // `PostDispatchInfo::calc_unspent` guards against such a case.2580                        match payed.offset(refund_imbalance) {2581                            Ok(actual_payment) => actual_payment,2582                            Err(_) => return Err(InvalidTransaction::Payment.into()),2583                        }2584                    }2585                    // We do not recreate the account using the refund. The up front payment2586                    // is gone in that case.2587                    Err(_) => payed,2588                };2589            let imbalances = actual_payment.split(tip);2590            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2591                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2592            );2593        }2594        Ok(())2595    }2596}25972598// #endregion
modifiedruntime_types.jsondiffbeforeafterboth
--- a/runtime_types.json
+++ b/runtime_types.json
@@ -42,18 +42,15 @@
       "Fraction": "u128"
     },
     "FungibleItemType": {
-      "Collection": "CollectionId",
       "Owner": "AccountId",
       "Value": "u128"
     },
     "NftItemType": {
-      "Collection": "CollectionId",
       "Owner": "AccountId",
       "ConstData": "Vec<u8>",
       "VariableData": "Vec<u8>"
     },
     "ReFungibleItemType": {
-      "Collection": "CollectionId",
       "Owner": "Vec<Ownership<AccountId>>",
       "ConstData": "Vec<u8>",
       "VariableData": "Vec<u8>"