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

difftreelog

source

pallets/nft/src/lib.rs87.7 KiBsourcehistory
1#![cfg_attr(not(feature = "std"), no_std)]23#[cfg(feature = "std")]4pub use std::*;56#[cfg(feature = "std")]7pub use serde::*;89use codec::{Decode, Encode};10pub use frame_support::{11    construct_runtime, decl_event, decl_module, decl_storage, decl_error,12    dispatch::DispatchResult,13    ensure, fail, parameter_types,14    traits::{15        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,16        Randomness, WithdrawReason,17    },18    weights::{19        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},20        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,21        WeightToFeePolynomial,22    },23    IsSubType, StorageValue,24};2526use frame_system::{self as system, ensure_signed, ensure_root};27use sp_runtime::sp_std::prelude::Vec;28use sp_runtime::{29    traits::{30        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SignedExtension, Zero,31    },32    transaction_validity::{33        InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,34    },35    FixedPointOperand, FixedU128,36};37use pallet_contracts::ContractAddressFor;38use sp_runtime::traits::StaticLookup;3940#[cfg(test)]41mod mock;4243#[cfg(test)]44mod tests;4546mod default_weights;4748// Structs49// #region5051#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]52#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]53pub enum CollectionMode {54    Invalid,55    NFT,56    // decimal points57    Fungible(u32),58    // decimal points59    ReFungible(u32),60}6162impl Into<u8> for CollectionMode {63    fn into(self) -> u8 {64        match self {65            CollectionMode::Invalid => 0,66            CollectionMode::NFT => 1,67            CollectionMode::Fungible(_) => 2,68            CollectionMode::ReFungible(_) => 3,69        }70    }71}7273#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]74#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]75pub enum AccessMode {76    Normal,77    WhiteList,78}79impl Default for AccessMode {80    fn default() -> Self {81        Self::Normal82    }83}8485impl Default for CollectionMode {86    fn default() -> Self {87        Self::Invalid88    }89}9091#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]92#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]93pub struct Ownership<AccountId> {94    pub owner: AccountId,95    pub fraction: u128,96}9798#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]99#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]100pub struct CollectionType<AccountId> {101    pub owner: AccountId,102    pub mode: CollectionMode,103    pub access: AccessMode,104    pub decimal_points: u32,105    pub name: Vec<u16>,        // 64 include null escape char106    pub description: Vec<u16>, // 256 include null escape char107    pub token_prefix: Vec<u8>, // 16 include null escape char108    pub mint_mode: bool,109    pub offchain_schema: Vec<u8>,110    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender111    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship112    pub variable_on_chain_schema: Vec<u8>, //113    pub const_on_chain_schema: Vec<u8>, //114}115116#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]117#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]118pub struct NftItemType<AccountId> {119    pub collection: u64,120    pub owner: AccountId,121    pub const_data: Vec<u8>,122    pub variable_data: Vec<u8>,123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct FungibleItemType<AccountId> {128    pub collection: u64,129    pub owner: AccountId,130    pub value: u128,131}132133#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]134#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]135pub struct ReFungibleItemType<AccountId> {136    pub collection: u64,137    pub owner: Vec<Ownership<AccountId>>,138    pub const_data: Vec<u8>,139    pub variable_data: Vec<u8>,140}141142#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]143#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]144pub struct ApprovePermissions<AccountId> {145    pub approved: AccountId,146    pub amount: u64,147}148149#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]150#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]151pub struct VestingItem<AccountId, Moment> {152    pub sender: AccountId,153    pub recipient: AccountId,154    pub collection_id: u64,155    pub item_id: u64,156    pub amount: u64,157    pub vesting_date: Moment,158}159160#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]161#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]162pub struct BasketItem<AccountId, BlockNumber> {163    pub address: AccountId,164    pub start_block: BlockNumber,165}166167#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]168#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169pub struct ChainLimits {170    pub collection_numbers_limit: u64,171    pub account_token_ownership_limit: u64,172    pub collections_admins_limit: u64,173    pub custom_data_limit: u32,174175    // Timeouts for item types in passed blocks176    pub nft_sponsor_transfer_timeout: u32,177    pub fungible_sponsor_transfer_timeout: u32,178    pub refungible_sponsor_transfer_timeout: u32,179}180181pub trait WeightInfo {182	fn create_collection() -> Weight;183	fn destroy_collection() -> Weight;184	fn add_to_white_list() -> Weight;185	fn remove_from_white_list() -> Weight;186    fn set_public_access_mode() -> Weight;187    fn set_mint_permission() -> Weight;188    fn change_collection_owner() -> Weight;189    fn add_collection_admin() -> Weight;190    fn remove_collection_admin() -> Weight;191    fn set_collection_sponsor() -> Weight;192    fn confirm_sponsorship() -> Weight;193    fn remove_collection_sponsor() -> Weight;194    fn create_item(s: usize) -> Weight;195    fn burn_item() -> Weight;196    fn transfer() -> Weight;197    fn approve() -> Weight;198    fn transfer_from() -> Weight;199    fn set_offchain_schema() -> Weight;200    fn set_const_on_chain_schema() -> Weight;201    fn set_variable_on_chain_schema() -> Weight;202    fn set_variable_meta_data() -> Weight;203    // fn enable_contract_sponsoring() -> Weight;204}205206#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]207#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]208pub struct CreateNftData {209    pub const_data: Vec<u8>,210    pub variable_data: Vec<u8>,211}212213#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]214#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]215pub struct CreateFungibleData {216}217218#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]219#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]220pub struct CreateReFungibleData {221    pub const_data: Vec<u8>,222    pub variable_data: Vec<u8>,223}224225#[derive(Encode, Decode, Debug, Clone, PartialEq)]226#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]227pub enum CreateItemData {228    NFT(CreateNftData),229    Fungible(CreateFungibleData),230    ReFungible(CreateReFungibleData)231}232233impl CreateItemData {234    pub fn len(&self) -> usize {235        let len = match self {236            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),237            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),238            _ => 0239        };240        241        return len;242    }243}244245impl From<CreateNftData> for CreateItemData {246    fn from(item: CreateNftData) -> Self {247        CreateItemData::NFT(item)248    }249}250251impl From<CreateReFungibleData> for CreateItemData {252    fn from(item: CreateReFungibleData) -> Self {253        CreateItemData::ReFungible(item)254    }255}256257impl From<CreateFungibleData> for CreateItemData {258    fn from(item: CreateFungibleData) -> Self {259        CreateItemData::Fungible(item)260    }261}262263264decl_error! {265	/// Error for non-fungible-token module.266	pub enum Error for Module<T: Trait> {267        /// Total collections bound exceeded.268        TotalCollectionsLimitExceeded,269		/// Decimal_points parameter must be lower than 4.270        CollectionDecimalPointLimitExceeded, 271        /// Collection name can not be longer than 63 char.272        CollectionNameLimitExceeded, 273        /// Collection description can not be longer than 255 char.274        CollectionDescriptionLimitExceeded, 275        /// Token prefix can not be longer than 15 char.276        CollectionTokenPrefixLimitExceeded,277        /// This collection does not exist.278        CollectionNotFound,279        /// Item not exists.280        TokenNotFound,281        /// Arithmetic calculation overflow.282        NumOverflow,       283        /// Account already has admin role.284        AlreadyAdmin,  285        /// You do not own this collection.286        NoPermission,287        /// This address is not set as sponsor, use setCollectionSponsor first.288        ConfirmUnsetSponsorFail,289        /// Collection is not in mint mode.290        PublicMintingNotAllowed,291        /// Sender parameter and item owner must be equal.292        MustBeTokenOwner,293        /// Item balance not enough.294        TokenValueTooLow,295        /// Size of item is too large.296        NftSizeLimitExceeded,297        /// No approve found298        ApproveNotFound,299        /// Requested value more than approved.300        TokenValueNotEnough,301        /// Only approved addresses can call this method.302        ApproveRequired,303        /// Address is not in white list.304        AddresNotInWhiteList,305        /// Number of collection admins bound exceeded.306        CollectionAdminsLimitExceeded,307        /// Owned tokens by a single address bound exceeded.308        AddressOwnershipLimitExceeded,309        /// Length of items properties must be greater than 0.310        EmptyArgument,311        /// const_data exceeded data limit.312        TokenConstDataLimitExceeded,313        /// variable_data exceeded data limit.314        TokenVariableDataLimitExceeded,315        /// Not NFT item data used to mint in NFT collection.316        NotNftDataUsedToMintNftCollectionToken,317        /// Not Fungible item data used to mint in Fungible collection.318        NotFungibleDataUsedToMintFungibleCollectionToken,319        /// Not Re Fungible item data used to mint in Re Fungible collection.320        NotReFungibleDataUsedToMintReFungibleCollectionToken,321        /// Unexpected collection type.322        UnexpectedCollectionType,323        /// Can't store metadata in fungible tokens.324        CantStoreMetadataInFungibleTokens325	}326}327328pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {329    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;330331    /// Weight information for extrinsics in this pallet.332	type WeightInfo: WeightInfo;333}334335#[cfg(feature = "runtime-benchmarks")]336mod benchmarking;337338// #endregion339340decl_storage! {341    trait Store for Module<T: Trait> as Nft {342343        // Private members344        NextCollectionID: u64;345        CreatedCollectionCount: u64;346        ChainVersion: u64;347        ItemListIndex: map hasher(blake2_128_concat) u64 => u64;348349        // Chain limits struct350        pub ChainLimit get(fn chain_limit) config(): ChainLimits;351352        // Bound counters353        CollectionCount: u64;354        pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;355356        // Basic collections357        pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;358        pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;359        pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;360361        /// Balance owner per collection map362        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;363364        /// second parameter: item id + owner account id365        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;366367        /// Item collections368        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;369        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;370        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;371372        /// Index list373        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;374375        /// Tokens transfer baskets376        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;377        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => Vec<BasketItem<T::AccountId, T::BlockNumber>>;378        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;379380        // Contract Sponsorship and Ownership381        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;382        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;383        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;384        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;385    }386    add_extra_genesis {387        build(|config: &GenesisConfig<T>| {388            // Modification of storage389            for (_num, _c) in &config.collection {390                <Module<T>>::init_collection(_c);391            }392393            for (_num, _q, _i) in &config.nft_item_id {394                <Module<T>>::init_nft_token(_i);395            }396397            for (_num, _q, _i) in &config.fungible_item_id {398                <Module<T>>::init_fungible_token(_i);399            }400401            for (_num, _q, _i) in &config.refungible_item_id {402                <Module<T>>::init_refungible_token(_i);403            }404        })405    }406}407408decl_event!(409    pub enum Event<T>410    where411        AccountId = <T as system::Trait>::AccountId,412    {413        /// New collection was created414        /// 415        /// # Arguments416        /// 417        /// * collection_id: Globally unique identifier of newly created collection.418        /// 419        /// * mode: [CollectionMode] converted into u8.420        /// 421        /// * account_id: Collection owner.422        Created(u64, u8, AccountId),423424        /// New item was created.425        /// 426        /// # Arguments427        /// 428        /// * collection_id: Id of the collection where item was created.429        /// 430        /// * item_id: Id of an item. Unique within the collection.431        ItemCreated(u64, u64),432433        /// Collection item was burned.434        /// 435        /// # Arguments436        /// 437        /// collection_id.438        /// 439        /// item_id: Identifier of burned NFT.440        ItemDestroyed(u64, u64),441    }442);443444decl_module! {445    pub struct Module<T: Trait> for enum Call where origin: T::Origin {446447        fn deposit_event() = default;448        type Error = Error<T>;449450        fn on_initialize(now: T::BlockNumber) -> Weight {451452            if ChainVersion::get() < 2453            {454                let value = NextCollectionID::get();455                CreatedCollectionCount::put(value);456                ChainVersion::put(2);457            }458459            0460        }461462        /// 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.463        /// 464        /// # Permissions465        /// 466        /// * Anyone.467        /// 468        /// # Arguments469        /// 470        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.471        /// 472        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.473        /// 474        /// * token_prefix: UTF-8 string with token prefix.475        /// 476        /// * mode: [CollectionMode] collection type and type dependent data.477        // returns collection ID478        #[weight = T::WeightInfo::create_collection()]479        pub fn create_collection(origin,480                                 collection_name: Vec<u16>,481                                 collection_description: Vec<u16>,482                                 token_prefix: Vec<u8>,483                                 mode: CollectionMode) -> DispatchResult {484485            // Anyone can create a collection486            let who = ensure_signed(origin)?;487488            let decimal_points = match mode {489                CollectionMode::Fungible(points) => points,490                CollectionMode::ReFungible(points) => points,491                _ => 0492            };493494            // bound Total number of collections495            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);496497            // check params498            ensure!(decimal_points <= 4, Error::<T>::CollectionDecimalPointLimitExceeded);499500            let mut name = collection_name.to_vec();501            name.push(0);502            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);503504            let mut description = collection_description.to_vec();505            description.push(0);506            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);507508            let mut prefix = token_prefix.to_vec();509            prefix.push(0);510            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);511512            // Generate next collection ID513            let next_id = CreatedCollectionCount::get()514                .checked_add(1)515                .ok_or(Error::<T>::NumOverflow)?;516517            // bound counter518            let total = CollectionCount::get()519                .checked_add(1)520                .ok_or(Error::<T>::NumOverflow)?;521522            CreatedCollectionCount::put(next_id);523            CollectionCount::put(total);524525            // Create new collection526            let new_collection = CollectionType {527                owner: who.clone(),528                name: name,529                mode: mode.clone(),530                mint_mode: false,531                access: AccessMode::Normal,532                description: description,533                decimal_points: decimal_points,534                token_prefix: prefix,535                offchain_schema: Vec::new(),536                sponsor: T::AccountId::default(),537                unconfirmed_sponsor: T::AccountId::default(),538                variable_on_chain_schema: Vec::new(),539                const_on_chain_schema: Vec::new(),540            };541542            // Add new collection to map543            <Collection<T>>::insert(next_id, new_collection);544545            // call event546            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));547548            Ok(())549        }550551        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.552        /// 553        /// # Permissions554        /// 555        /// * Collection Owner.556        /// 557        /// # Arguments558        /// 559        /// * collection_id: collection to destroy.560        #[weight = T::WeightInfo::destroy_collection()]561        pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {562563            let sender = ensure_signed(origin)?;564            Self::check_owner_permissions(collection_id, sender)?;565566            <AddressTokens<T>>::remove_prefix(collection_id);567            <ApprovedList<T>>::remove_prefix(collection_id);568            <Balance<T>>::remove_prefix(collection_id);569            <ItemListIndex>::remove(collection_id);570            <AdminList<T>>::remove(collection_id);571            <Collection<T>>::remove(collection_id);572            <WhiteList<T>>::remove(collection_id);573574            <NftItemList<T>>::remove_prefix(collection_id);575            <FungibleItemList<T>>::remove_prefix(collection_id);576            <ReFungibleItemList<T>>::remove_prefix(collection_id);577578            <NftTransferBasket<T>>::remove_prefix(collection_id);579            <FungibleTransferBasket<T>>::remove_prefix(collection_id);580            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);581582            if CollectionCount::get() > 0583            {584                // bound couter585                let total = CollectionCount::get()586                    .checked_sub(1)587                    .ok_or(Error::<T>::NumOverflow)?;588589                CollectionCount::put(total);590            }591592            Ok(())593        }594595        /// Add an address to white list.596        /// 597        /// # Permissions598        /// 599        /// * Collection Owner600        /// * Collection Admin601        /// 602        /// # Arguments603        /// 604        /// * collection_id.605        /// 606        /// * address.607        #[weight = T::WeightInfo::add_to_white_list()]608        pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{609610            let sender = ensure_signed(origin)?;611            Self::check_owner_or_admin_permissions(collection_id, sender)?;612613            let mut white_list_collection: Vec<T::AccountId>;614            if <WhiteList<T>>::contains_key(collection_id) {615                white_list_collection = <WhiteList<T>>::get(collection_id);616                if !white_list_collection.contains(&address.clone())617                {618                    white_list_collection.push(address.clone());619                }620            }621            else {622                white_list_collection = Vec::new();623                white_list_collection.push(address.clone());624            }625626            <WhiteList<T>>::insert(collection_id, white_list_collection);627            Ok(())628        }629630        /// Remove an address from white list.631        /// 632        /// # Permissions633        /// 634        /// * Collection Owner635        /// * Collection Admin636        /// 637        /// # Arguments638        /// 639        /// * collection_id.640        /// 641        /// * address.642        #[weight = T::WeightInfo::remove_from_white_list()]643        pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{644645            let sender = ensure_signed(origin)?;646            Self::check_owner_or_admin_permissions(collection_id, sender)?;647648            if <WhiteList<T>>::contains_key(collection_id) {649                let mut white_list_collection = <WhiteList<T>>::get(collection_id);650                if white_list_collection.contains(&address.clone())651                {652                    white_list_collection.retain(|i| *i != address.clone());653                    <WhiteList<T>>::insert(collection_id, white_list_collection);654                }655            }656657            Ok(())658        }659660        /// Toggle between normal and white list access for the methods with access for `Anyone`.661        /// 662        /// # Permissions663        /// 664        /// * Collection Owner.665        /// 666        /// # Arguments667        /// 668        /// * collection_id.669        /// 670        /// * mode: [AccessMode]671        #[weight = T::WeightInfo::set_public_access_mode()]672        pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult673        {674            let sender = ensure_signed(origin)?;675676            Self::check_owner_permissions(collection_id, sender)?;677            let mut target_collection = <Collection<T>>::get(collection_id);678            target_collection.access = mode;679            <Collection<T>>::insert(collection_id, target_collection);680681            Ok(())682        }683684        /// Allows Anyone to create tokens if:685        /// * White List is enabled, and686        /// * Address is added to white list, and687        /// * This method was called with True parameter688        /// 689        /// # Permissions690        /// * Collection Owner691        ///692        /// # Arguments693        /// 694        /// * collection_id.695        /// 696        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.697        #[weight = T::WeightInfo::set_mint_permission()]698        pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult699        {700            let sender = ensure_signed(origin)?;701702            Self::check_owner_permissions(collection_id, sender)?;703            let mut target_collection = <Collection<T>>::get(collection_id);704            target_collection.mint_mode = mint_permission;705            <Collection<T>>::insert(collection_id, target_collection);706707            Ok(())708        }709710        /// Change the owner of the collection.711        /// 712        /// # Permissions713        /// 714        /// * Collection Owner.715        /// 716        /// # Arguments717        /// 718        /// * collection_id.719        /// 720        /// * new_owner.721        #[weight = T::WeightInfo::change_collection_owner()]722        pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {723724            let sender = ensure_signed(origin)?;725            Self::check_owner_permissions(collection_id, sender)?;726            let mut target_collection = <Collection<T>>::get(collection_id);727            target_collection.owner = new_owner;728            <Collection<T>>::insert(collection_id, target_collection);729730            Ok(())731        }732733        /// Adds an admin of the Collection.734        /// 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. 735        /// 736        /// # Permissions737        /// 738        /// * Collection Owner.739        /// * Collection Admin.740        /// 741        /// # Arguments742        /// 743        /// * collection_id: ID of the Collection to add admin for.744        /// 745        /// * new_admin_id: Address of new admin to add.746        #[weight = T::WeightInfo::add_collection_admin()]747        pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {748749            let sender = ensure_signed(origin)?;750            Self::check_owner_or_admin_permissions(collection_id, sender)?;751            let mut admin_arr: Vec<T::AccountId> = Vec::new();752753            if <AdminList<T>>::contains_key(collection_id)754            {755                admin_arr = <AdminList<T>>::get(collection_id);756                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);757            }758759            // Number of collection admins760            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);761762            admin_arr.push(new_admin_id);763            <AdminList<T>>::insert(collection_id, admin_arr);764765            Ok(())766        }767768        /// 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.769        ///770        /// # Permissions771        /// 772        /// * Collection Owner.773        /// * Collection Admin.774        /// 775        /// # Arguments776        /// 777        /// * collection_id: ID of the Collection to remove admin for.778        /// 779        /// * account_id: Address of admin to remove.780        #[weight = T::WeightInfo::remove_collection_admin()]781        pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {782783            let sender = ensure_signed(origin)?;784            Self::check_owner_or_admin_permissions(collection_id, sender)?;785786            if <AdminList<T>>::contains_key(collection_id)787            {788                let mut admin_arr = <AdminList<T>>::get(collection_id);789                admin_arr.retain(|i| *i != account_id);790                <AdminList<T>>::insert(collection_id, admin_arr);791            }792793            Ok(())794        }795796        /// # Permissions797        /// 798        /// * Collection Owner799        /// 800        /// # Arguments801        /// 802        /// * collection_id.803        /// 804        /// * new_sponsor.805        #[weight = T::WeightInfo::set_collection_sponsor()]806        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {807808            let sender = ensure_signed(origin)?;809            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);810811            let mut target_collection = <Collection<T>>::get(collection_id);812            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);813814            target_collection.unconfirmed_sponsor = new_sponsor;815            <Collection<T>>::insert(collection_id, target_collection);816817            Ok(())818        }819820        /// # Permissions821        /// 822        /// * Sponsor.823        /// 824        /// # Arguments825        /// 826        /// * collection_id.827        #[weight = T::WeightInfo::confirm_sponsorship()]828        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {829830            let sender = ensure_signed(origin)?;831            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);832833            let mut target_collection = <Collection<T>>::get(collection_id);834            ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);835836            target_collection.sponsor = target_collection.unconfirmed_sponsor;837            target_collection.unconfirmed_sponsor = T::AccountId::default();838            <Collection<T>>::insert(collection_id, target_collection);839840            Ok(())841        }842843        /// Switch back to pay-per-own-transaction model.844        ///845        /// # Permissions846        ///847        /// * Collection owner.848        /// 849        /// # Arguments850        /// 851        /// * collection_id.852        #[weight = T::WeightInfo::remove_collection_sponsor()]853        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {854855            let sender = ensure_signed(origin)?;856            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);857858            let mut target_collection = <Collection<T>>::get(collection_id);859            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);860861            target_collection.sponsor = T::AccountId::default();862            <Collection<T>>::insert(collection_id, target_collection);863864            Ok(())865        }866867        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.868        /// 869        /// # Permissions870        /// 871        /// * Collection Owner.872        /// * Collection Admin.873        /// * Anyone if874        ///     * White List is enabled, and875        ///     * Address is added to white list, and876        ///     * MintPermission is enabled (see SetMintPermission method)877        /// 878        /// # Arguments879        /// 880        /// * collection_id: ID of the collection.881        /// 882        /// * owner: Address, initial owner of the NFT.883        ///884        /// * data: Token data to store on chain.885        // #[weight =886        // (130_000_000 as Weight)887        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))888        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))889        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]890891        #[weight = T::WeightInfo::create_item(data.len())]892        pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {893894            let sender = ensure_signed(origin)?;895896            Self::collection_exists(collection_id)?;897898            let target_collection = <Collection<T>>::get(collection_id);899900            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;901            Self::validate_create_item_args(&target_collection, &data)?;902            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;903904            Ok(())905        }906907        /// This method creates multiple instances of NFT Collection created with CreateCollection method.908        /// 909        /// # Permissions910        /// 911        /// * Collection Owner.912        /// * Collection Admin.913        /// * Anyone if914        ///     * White List is enabled, and915        ///     * Address is added to white list, and916        ///     * MintPermission is enabled (see SetMintPermission method)917        /// 918        /// # Arguments919        /// 920        /// * collection_id: ID of the collection.921        /// 922        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].923        /// 924        /// * owner: Address, initial owner of the NFT.925        #[weight = T::WeightInfo::create_item(items_data.into_iter()926                               .map(|data| { data.len() })927                               .sum())]928        pub fn create_multiple_items(origin, collection_id: u64, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {929930            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);931            let sender = ensure_signed(origin)?;932933            Self::collection_exists(collection_id)?;934            let target_collection = <Collection<T>>::get(collection_id);935936            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;937938            for data in &items_data {939                Self::validate_create_item_args(&target_collection, data)?;940            }941            for data in &items_data {942                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;943            }944945            Ok(())946        }947948        /// Destroys a concrete instance of NFT.949        /// 950        /// # Permissions951        /// 952        /// * Collection Owner.953        /// * Collection Admin.954        /// * Current NFT Owner.955        /// 956        /// # Arguments957        /// 958        /// * collection_id: ID of the collection.959        /// 960        /// * item_id: ID of NFT to burn.961        #[weight = T::WeightInfo::burn_item()]962        pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {963964            let sender = ensure_signed(origin)?;965            Self::collection_exists(collection_id)?;966967            // Transfer permissions check968            let target_collection = <Collection<T>>::get(collection_id);969            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||970                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),971                Error::<T>::NoPermission);972973            if target_collection.access == AccessMode::WhiteList {974                Self::check_white_list(collection_id, &sender)?;975            }976977            match target_collection.mode978            {979                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,980                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,981                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,982                _ => ()983            };984985            // call event986            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));987988            Ok(())989        }990991        /// Change ownership of the token.992        /// 993        /// # Permissions994        /// 995        /// * Collection Owner996        /// * Collection Admin997        /// * Current NFT owner998        ///999        /// # Arguments1000        /// 1001        /// * recipient: Address of token recipient.1002        /// 1003        /// * collection_id.1004        /// 1005        /// * item_id: ID of the item1006        ///     * Non-Fungible Mode: Required.1007        ///     * Fungible Mode: Ignored.1008        ///     * Re-Fungible Mode: Required.1009        /// 1010        /// * value: Amount to transfer.1011        ///     * Non-Fungible Mode: Ignored1012        ///     * Fungible Mode: Must specify transferred amount1013        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1014        #[weight = T::WeightInfo::transfer()]1015        pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {10161017            let sender = ensure_signed(origin)?;10181019            // Transfer permissions check1020            let target_collection = <Collection<T>>::get(collection_id);1021            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1022                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1023                Error::<T>::NoPermission);10241025            if target_collection.access == AccessMode::WhiteList {1026                Self::check_white_list(collection_id, &sender)?;1027                Self::check_white_list(collection_id, &recipient)?;1028            }10291030            match target_collection.mode1031            {1032                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1033                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1034                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1035                _ => ()1036            };10371038            Ok(())1039        }10401041        /// Set, change, or remove approved address to transfer the ownership of the NFT.1042        /// 1043        /// # Permissions1044        /// 1045        /// * Collection Owner1046        /// * Collection Admin1047        /// * Current NFT owner1048        /// 1049        /// # Arguments1050        /// 1051        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1052        /// 1053        /// * collection_id.1054        /// 1055        /// * item_id: ID of the item.1056        #[weight = T::WeightInfo::approve()]1057        pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {10581059            let sender = ensure_signed(origin)?;10601061            // Transfer permissions check1062            let target_collection = <Collection<T>>::get(collection_id);1063            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1064                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1065                Error::<T>::NoPermission);10661067            if target_collection.access == AccessMode::WhiteList {1068                Self::check_white_list(collection_id, &sender)?;1069                Self::check_white_list(collection_id, &approved)?;1070            }10711072            // amount param stub1073            let amount = 100000000;10741075            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1076            if list_exists {10771078                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1079                let item_contains = list.iter().any(|i| i.approved == approved);10801081                if !item_contains {1082                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1083                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1084                }1085            } else {10861087                let mut list = Vec::new();1088                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1089                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1090            }10911092            Ok(())1093        }1094        1095        /// 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.1096        /// 1097        /// # Permissions1098        /// * Collection Owner1099        /// * Collection Admin1100        /// * Current NFT owner1101        /// * Address approved by current NFT owner1102        /// 1103        /// # Arguments1104        /// 1105        /// * from: Address that owns token.1106        /// 1107        /// * recipient: Address of token recipient.1108        /// 1109        /// * collection_id.1110        /// 1111        /// * item_id: ID of the item.1112        /// 1113        /// * value: Amount to transfer.1114        #[weight = T::WeightInfo::transfer_from()]1115        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {11161117            let sender = ensure_signed(origin)?;1118            let mut appoved_transfer = false;11191120            // Check approve1121            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1122                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1123                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1124                if opt_item.is_some()1125                {1126                    appoved_transfer = true;1127                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1128                }1129            }11301131            // Transfer permissions check1132            let target_collection = <Collection<T>>::get(collection_id);1133                ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1134                Error::<T>::NoPermission);11351136            if target_collection.access == AccessMode::WhiteList {1137                Self::check_white_list(collection_id, &sender)?;1138                Self::check_white_list(collection_id, &recipient)?;1139            }11401141            // remove approve1142            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1143                .into_iter().filter(|i| i.approved != sender.clone()).collect();1144            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);114511461147            match target_collection.mode1148            {1149                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1150                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1151                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1152                _ => ()1153            };11541155            Ok(())1156        }11571158        ///1159        #[weight = 0]1160        pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11611162            // let no_perm_mes = "You do not have permissions to modify this collection";1163            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1164            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1165            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11661167            // // on_nft_received  call11681169            // Self::transfer(origin, collection_id, item_id, new_owner)?;11701171            Ok(())1172        }11731174        /// Set off-chain data schema.1175        /// 1176        /// # Permissions1177        /// 1178        /// * Collection Owner1179        /// * Collection Admin1180        /// 1181        /// # Arguments1182        /// 1183        /// * collection_id.1184        /// 1185        /// * schema: String representing the offchain data schema.1186        #[weight = T::WeightInfo::set_variable_meta_data()]1187        pub fn set_variable_meta_data (1188            origin,1189            collection_id: u64,1190            item_id: u64,1191            data: Vec<u8>1192        ) -> DispatchResult {1193            let sender = ensure_signed(origin)?;1194            1195            Self::collection_exists(collection_id)?;1196            1197            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);11981199            // Modify permissions check1200            let target_collection = <Collection<T>>::get(collection_id);1201            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1202                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1203                Error::<T>::NoPermission);12041205            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12061207            match target_collection.mode1208            {1209                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1210                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1211                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1212                _ => fail!(Error::<T>::UnexpectedCollectionType)1213            };12141215            Ok(())1216        }1217        12181219        /// Set off-chain data schema.1220        /// 1221        /// # Permissions1222        /// 1223        /// * Collection Owner1224        /// * Collection Admin1225        /// 1226        /// # Arguments1227        /// 1228        /// * collection_id.1229        /// 1230        /// * schema: String representing the offchain data schema.1231        #[weight = T::WeightInfo::set_offchain_schema()]1232        pub fn set_offchain_schema(1233            origin,1234            collection_id: u64,1235            schema: Vec<u8>1236        ) -> DispatchResult {1237            let sender = ensure_signed(origin)?;1238            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12391240            let mut target_collection = <Collection<T>>::get(collection_id);1241            target_collection.offchain_schema = schema;1242            <Collection<T>>::insert(collection_id, target_collection);12431244            Ok(())1245        }12461247        /// Set const on-chain data schema.1248        /// 1249        /// # Permissions1250        /// 1251        /// * Collection Owner1252        /// * Collection Admin1253        /// 1254        /// # Arguments1255        /// 1256        /// * collection_id.1257        /// 1258        /// * schema: String representing the const on-chain data schema.1259        #[weight = T::WeightInfo::set_const_on_chain_schema()]1260        pub fn set_const_on_chain_schema (1261            origin,1262            collection_id: u64,1263            schema: Vec<u8>1264        ) -> DispatchResult {1265            let sender = ensure_signed(origin)?;1266            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12671268            let mut target_collection = <Collection<T>>::get(collection_id);1269            target_collection.const_on_chain_schema = schema;1270            <Collection<T>>::insert(collection_id, target_collection);12711272            Ok(())1273        }12741275        /// Set variable on-chain data schema.1276        /// 1277        /// # Permissions1278        /// 1279        /// * Collection Owner1280        /// * Collection Admin1281        /// 1282        /// # Arguments1283        /// 1284        /// * collection_id.1285        /// 1286        /// * schema: String representing the variable on-chain data schema.1287        #[weight = T::WeightInfo::set_const_on_chain_schema()]1288        pub fn set_variable_on_chain_schema (1289            origin,1290            collection_id: u64,1291            schema: Vec<u8>1292        ) -> DispatchResult {1293            let sender = ensure_signed(origin)?;1294            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12951296            let mut target_collection = <Collection<T>>::get(collection_id);1297            target_collection.variable_on_chain_schema = schema;1298            <Collection<T>>::insert(collection_id, target_collection);12991300            Ok(())1301        }13021303        // Sudo permissions function1304        #[weight = 0]1305        pub fn set_chain_limits(1306            origin,1307            limits: ChainLimits1308        ) -> DispatchResult {1309            ensure_root(origin)?;1310            <ChainLimit>::put(limits);1311            Ok(())1312        }13131314        /// Enable smart contract self-sponsoring.1315        /// 1316        /// # Permissions1317        /// 1318        /// * Contract Owner1319        /// 1320        /// # Arguments1321        /// 1322        /// * contract address1323        /// * enable flag1324        /// 1325        #[weight = 0]1326        pub fn enable_contract_sponsoring(1327            origin,1328            contract_address: T::AccountId,1329            enable: bool1330        ) -> DispatchResult {1331            let sender = ensure_signed(origin)?;1332            let mut is_owner = false;1333            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1334                let owner = <ContractOwner<T>>::get(&contract_address);1335                is_owner = sender == owner;1336            }1337            ensure!(is_owner, Error::<T>::NoPermission);13381339            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1340            Ok(())1341        }13421343        /// Set the rate limit for contract sponsoring to specified number of blocks.1344        /// 1345        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1346        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1347        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1348        /// from contract endowment if there are at least B blocks between such transactions. 1349        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1350        /// 1351        /// # Permissions1352        /// 1353        /// * Contract Owner1354        /// 1355        /// # Arguments1356        /// 1357        /// -`contract_address`: Address of the contract to sponsor1358        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1359        /// 1360        #[weight = 0]1361        pub fn set_contract_sponsoring_rate_limit(1362            origin,1363            contract_address: T::AccountId,1364            rate_limit: T::BlockNumber1365        ) -> DispatchResult {1366            let sender = ensure_signed(origin)?;1367            let mut is_owner = false;1368            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1369                let owner = <ContractOwner<T>>::get(&contract_address);1370                is_owner = sender == owner;1371            }1372            ensure!(is_owner, Error::<T>::NoPermission);13731374            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1375            Ok(())1376        }13771378    }1379}13801381impl<T: Trait> Module<T> {13821383    fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {13841385        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1386            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1387            Self::check_white_list(collection_id, owner)?;1388            Self::check_white_list(collection_id, sender)?;1389        }13901391        Ok(())1392    }13931394    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1395        match target_collection.mode1396        {1397            CollectionMode::NFT => {1398                if let CreateItemData::NFT(data) = data {1399                    // check sizes1400                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1401                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1402                } else {1403                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1404                }1405            },1406            CollectionMode::Fungible(_) => {1407                if let CreateItemData::Fungible(_) = data {1408                } else {1409                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1410                }1411            },1412            CollectionMode::ReFungible(_) => {1413                if let CreateItemData::ReFungible(data) = data {14141415                    // check sizes1416                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1417                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1418                } else {1419                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1420                }1421            },1422            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1423        };14241425        Ok(())1426    }14271428    fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1429        match data1430        {1431            CreateItemData::NFT(data) => {1432                let item = NftItemType {1433                    collection: collection_id,1434                    owner,1435                    const_data: data.const_data,1436                    variable_data: data.variable_data1437                };14381439                Self::add_nft_item(item)?;1440            },1441            CreateItemData::Fungible(_) => {1442                let item = FungibleItemType {1443                    collection: collection_id,1444                    owner,1445                    value: (10 as u128).pow(collection.decimal_points)1446                };14471448                Self::add_fungible_item(item)?;1449            },1450            CreateItemData::ReFungible(data) => {1451                let mut owner_list = Vec::new();1452                let value = (10 as u128).pow(collection.decimal_points);1453                owner_list.push(Ownership {owner: owner.clone(), fraction: value});14541455                let item = ReFungibleItemType {1456                    collection: collection_id,1457                    owner: owner_list,1458                    const_data: data.const_data,1459                    variable_data: data.variable_data1460                };14611462                Self::add_refungible_item(item)?;1463            }1464        };146514661467        // call event1468        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));14691470        Ok(())1471    }14721473    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1474        let current_index = <ItemListIndex>::get(item.collection)1475            .checked_add(1)1476            .ok_or(Error::<T>::NumOverflow)?;1477        let itemcopy = item.clone();1478        let owner = item.owner.clone();1479        let value = item.value as u64;14801481        Self::add_token_index(item.collection, current_index, owner.clone())?;14821483        <ItemListIndex>::insert(item.collection, current_index);1484        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14851486        // Add current block1487        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1488        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1489        1490        // Update balance1491        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1492            .checked_add(value)1493            .ok_or(Error::<T>::NumOverflow)?;1494        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14951496        Ok(())1497    }14981499    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1500        let current_index = <ItemListIndex>::get(item.collection)1501            .checked_add(1)1502            .ok_or(Error::<T>::NumOverflow)?;1503        let itemcopy = item.clone();15041505        let value = item.owner.first().unwrap().fraction as u64;1506        let owner = item.owner.first().unwrap().owner.clone();15071508        Self::add_token_index(item.collection, current_index, owner.clone())?;15091510        <ItemListIndex>::insert(item.collection, current_index);1511        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15121513        // Add current block1514        let block_number: T::BlockNumber = 0.into();1515        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);15161517        // Update balance1518        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1519            .checked_add(value)1520            .ok_or(Error::<T>::NumOverflow)?;1521        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15221523        Ok(())1524    }15251526    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1527        let current_index = <ItemListIndex>::get(item.collection)1528            .checked_add(1)1529            .ok_or(Error::<T>::NumOverflow)?;15301531        let item_owner = item.owner.clone();1532        let collection_id = item.collection.clone();1533        Self::add_token_index(collection_id, current_index, item.owner.clone())?;15341535        <ItemListIndex>::insert(collection_id, current_index);1536        <NftItemList<T>>::insert(collection_id, current_index, item);15371538        // Add current block1539        let block_number: T::BlockNumber = 0.into();1540        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);15411542        // Update balance1543        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1544            .checked_add(1)1545            .ok_or(Error::<T>::NumOverflow)?;1546        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);15471548        Ok(())1549    }15501551    fn burn_refungible_item(1552        collection_id: u64,1553        item_id: u64,1554        owner: T::AccountId,1555    ) -> DispatchResult {1556        ensure!(1557            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1558            Error::<T>::TokenNotFound1559        );1560        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1561        let item = collection1562            .owner1563            .iter()1564            .filter(|&i| i.owner == owner)1565            .next()1566            .unwrap();1567        Self::remove_token_index(collection_id, item_id, owner.clone())?;15681569        // remove approve list1570        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));15711572        // update balance1573        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1574            .checked_sub(item.fraction as u64)1575            .ok_or(Error::<T>::NumOverflow)?;1576        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15771578        <ReFungibleItemList<T>>::remove(collection_id, item_id);15791580        Ok(())1581    }15821583    fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1584        ensure!(1585            <NftItemList<T>>::contains_key(collection_id, item_id),1586            Error::<T>::TokenNotFound1587        );1588        let item = <NftItemList<T>>::get(collection_id, item_id);1589        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15901591        // remove approve list1592        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15931594        // update balance1595        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1596            .checked_sub(1)1597            .ok_or(Error::<T>::NumOverflow)?;1598        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1599        <NftItemList<T>>::remove(collection_id, item_id);16001601        Ok(())1602    }16031604    fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1605        ensure!(1606            <FungibleItemList<T>>::contains_key(collection_id, item_id),1607            Error::<T>::TokenNotFound1608        );1609        let item = <FungibleItemList<T>>::get(collection_id, item_id);1610        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16111612        // remove approve list1613        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16141615        // update balance1616        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1617            .checked_sub(item.value as u64)1618            .ok_or(Error::<T>::NumOverflow)?;1619        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);16201621        <FungibleItemList<T>>::remove(collection_id, item_id);16221623        Ok(())1624    }16251626    fn collection_exists(collection_id: u64) -> DispatchResult {1627        ensure!(1628            <Collection<T>>::contains_key(collection_id),1629            Error::<T>::CollectionNotFound1630        );1631        Ok(())1632    }16331634    fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1635        Self::collection_exists(collection_id)?;16361637        let target_collection = <Collection<T>>::get(collection_id);1638        ensure!(1639            subject == target_collection.owner,1640            Error::<T>::NoPermission1641        );16421643        Ok(())1644    }16451646    fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1647        let target_collection = <Collection<T>>::get(collection_id);1648        let mut result: bool = subject == target_collection.owner;1649        let exists = <AdminList<T>>::contains_key(collection_id);16501651        if !result & exists {1652            if <AdminList<T>>::get(collection_id).contains(&subject) {1653                result = true1654            }1655        }16561657        result1658    }16591660    fn check_owner_or_admin_permissions(1661        collection_id: u64,1662        subject: T::AccountId,1663    ) -> DispatchResult {1664        Self::collection_exists(collection_id)?;1665        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());16661667        ensure!(1668            result,1669            Error::<T>::NoPermission1670        );1671        Ok(())1672    }16731674    fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1675        let target_collection = <Collection<T>>::get(collection_id);16761677        match target_collection.mode {1678            CollectionMode::NFT => {1679                <NftItemList<T>>::get(collection_id, item_id).owner == subject1680            }1681            CollectionMode::Fungible(_) => {1682                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1683            }1684            CollectionMode::ReFungible(_) => {1685                <ReFungibleItemList<T>>::get(collection_id, item_id)1686                    .owner1687                    .iter()1688                    .any(|i| i.owner == subject)1689            }1690            CollectionMode::Invalid => false,1691        }1692    }16931694    fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1695        let mes = Error::<T>::AddresNotInWhiteList;1696        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1697        let wl = <WhiteList<T>>::get(collection_id);1698        ensure!(wl.contains(address), mes);16991700        Ok(())1701    }17021703    fn transfer_fungible(1704        collection_id: u64,1705        item_id: u64,1706        value: u64,1707        owner: T::AccountId,1708        new_owner: T::AccountId,1709    ) -> DispatchResult {1710        ensure!(1711            <FungibleItemList<T>>::contains_key(collection_id, item_id),1712            Error::<T>::TokenNotFound1713        );17141715        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1716        let amount = full_item.value;17171718        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);17191720        // update balance1721        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1722            .checked_sub(value)1723            .ok_or(Error::<T>::NumOverflow)?;1724        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);17251726        let mut new_owner_account_id = 0;1727        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1728        if new_owner_items.len() > 0 {1729            new_owner_account_id = new_owner_items[0];1730        }17311732        let val64 = value.into();17331734        // transfer1735        if amount == val64 && new_owner_account_id == 0 {1736            // change owner1737            // new owner do not have account1738            let mut new_full_item = full_item.clone();1739            new_full_item.owner = new_owner.clone();1740            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17411742            // update balance1743            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1744                .checked_add(value)1745                .ok_or(Error::<T>::NumOverflow)?;1746            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17471748            // update index collection1749            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1750        } else {1751            let mut new_full_item = full_item.clone();1752            new_full_item.value -= val64;17531754            // separate amount1755            if new_owner_account_id > 0 {1756                // new owner has account1757                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1758                item.value += val64;17591760                // update balance1761                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1762                    .checked_add(value)1763                    .ok_or(Error::<T>::NumOverflow)?;1764                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17651766                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1767            } else {1768                // new owner do not have account1769                let item = FungibleItemType {1770                    collection: collection_id,1771                    owner: new_owner.clone(),1772                    value: val64,1773                };17741775                Self::add_fungible_item(item)?;1776            }17771778            if amount == val64 {1779                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;17801781                // remove approve list1782                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1783                <FungibleItemList<T>>::remove(collection_id, item_id);1784            }17851786            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1787        }17881789        Ok(())1790    }17911792    fn transfer_refungible(1793        collection_id: u64,1794        item_id: u64,1795        value: u64,1796        owner: T::AccountId,1797        new_owner: T::AccountId,1798    ) -> DispatchResult {1799        ensure!(1800            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1801            Error::<T>::TokenNotFound1802        );18031804        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1805        let item = full_item1806            .owner1807            .iter()1808            .filter(|i| i.owner == owner)1809            .next()1810            .ok_or(Error::<T>::NumOverflow)?;1811        let amount = item.fraction;18121813        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);18141815        // update balance1816        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1817            .checked_sub(value)1818            .ok_or(Error::<T>::NumOverflow)?;1819        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18201821        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1822            .checked_add(value)1823            .ok_or(Error::<T>::NumOverflow)?;1824        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18251826        let old_owner = item.owner.clone();1827        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1828        let val64 = value.into();18291830        // transfer1831        if amount == val64 && !new_owner_has_account {1832            // change owner1833            // new owner do not have account1834            let mut new_full_item = full_item.clone();1835            new_full_item1836                .owner1837                .iter_mut()1838                .find(|i| i.owner == owner)1839                .unwrap()1840                .owner = new_owner.clone();1841            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18421843            // update index collection1844            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1845        } else {1846            let mut new_full_item = full_item.clone();1847            new_full_item1848                .owner1849                .iter_mut()1850                .find(|i| i.owner == owner)1851                .unwrap()1852                .fraction -= val64;18531854            // separate amount1855            if new_owner_has_account {1856                // new owner has account1857                new_full_item1858                    .owner1859                    .iter_mut()1860                    .find(|i| i.owner == new_owner)1861                    .unwrap()1862                    .fraction += val64;1863            } else {1864                // new owner do not have account1865                new_full_item.owner.push(Ownership {1866                    owner: new_owner.clone(),1867                    fraction: val64,1868                });1869                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1870            }18711872            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1873        }18741875        Ok(())1876    }18771878    fn transfer_nft(1879        collection_id: u64,1880        item_id: u64,1881        sender: T::AccountId,1882        new_owner: T::AccountId,1883    ) -> DispatchResult {1884        ensure!(1885            <NftItemList<T>>::contains_key(collection_id, item_id),1886            Error::<T>::TokenNotFound1887        );18881889        let mut item = <NftItemList<T>>::get(collection_id, item_id);18901891        ensure!(1892            sender == item.owner,1893            Error::<T>::MustBeTokenOwner1894        );18951896        // update balance1897        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1898            .checked_sub(1)1899            .ok_or(Error::<T>::NumOverflow)?;1900        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19011902        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1903            .checked_add(1)1904            .ok_or(Error::<T>::NumOverflow)?;1905        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19061907        // change owner1908        let old_owner = item.owner.clone();1909        item.owner = new_owner.clone();1910        <NftItemList<T>>::insert(collection_id, item_id, item);19111912        // update index collection1913        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;19141915        // reset approved list1916        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1917        Ok(())1918    }1919    1920    fn item_exists(1921        collection_id: u64,1922        item_id: u64,1923        mode: &CollectionMode1924    ) -> DispatchResult {1925        match mode {1926            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1927            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1928            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1929            _ => ()1930        };1931        1932        Ok(())1933    }19341935    fn set_re_fungible_variable_data(1936        collection_id: u64,1937        item_id: u64,1938        data: Vec<u8>1939    ) -> DispatchResult {1940        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);19411942        item.variable_data = data;19431944        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);19451946        Ok(())1947    }19481949    fn set_nft_variable_data(1950        collection_id: u64,1951        item_id: u64,1952        data: Vec<u8>1953    ) -> DispatchResult {1954        let mut item = <NftItemList<T>>::get(collection_id, item_id);1955        1956        item.variable_data = data;19571958        <NftItemList<T>>::insert(collection_id, item_id, item);1959        1960        Ok(())1961    }19621963    fn init_collection(item: &CollectionType<T::AccountId>) {1964        // check params1965        assert!(1966            item.decimal_points <= 4,1967            "decimal_points parameter must be lower than 4"1968        );1969        assert!(1970            item.name.len() <= 64,1971            "Collection name can not be longer than 63 char"1972        );1973        assert!(1974            item.name.len() <= 256,1975            "Collection description can not be longer than 255 char"1976        );1977        assert!(1978            item.token_prefix.len() <= 16,1979            "Token prefix can not be longer than 15 char"1980        );19811982        // Generate next collection ID1983        let next_id = CreatedCollectionCount::get()1984            .checked_add(1)1985            .unwrap();19861987        CreatedCollectionCount::put(next_id);1988    }19891990    fn init_nft_token(item: &NftItemType<T::AccountId>) {1991        let current_index = <ItemListIndex>::get(item.collection)1992            .checked_add(1)1993            .unwrap();19941995        let item_owner = item.owner.clone();1996        let collection_id = item.collection.clone();1997        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();19981999        <ItemListIndex>::insert(collection_id, current_index);20002001        // Update balance2002        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2003            .checked_add(1)2004            .unwrap();2005        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2006    }20072008    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2009        let current_index = <ItemListIndex>::get(item.collection)2010            .checked_add(1)2011            .unwrap();2012        let owner = item.owner.clone();2013        let value = item.value as u64;20142015        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20162017        <ItemListIndex>::insert(item.collection, current_index);20182019        // Update balance2020        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2021            .checked_add(value)2022            .unwrap();2023        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2024    }20252026    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2027        let current_index = <ItemListIndex>::get(item.collection)2028            .checked_add(1)2029            .unwrap();20302031        let value = item.owner.first().unwrap().fraction as u64;2032        let owner = item.owner.first().unwrap().owner.clone();20332034        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20352036        <ItemListIndex>::insert(item.collection, current_index);20372038        // Update balance2039        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2040            .checked_add(value)2041            .unwrap();2042        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2043    }20442045    fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {20462047        // add to account limit2048        if <AccountItemCount<T>>::contains_key(owner.clone()) {20492050            // bound Owned tokens by a single address2051            let count = <AccountItemCount<T>>::get(owner.clone());2052            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);20532054            <AccountItemCount<T>>::insert(owner.clone(), count2055                .checked_add(1)2056                .ok_or(Error::<T>::NumOverflow)?);2057        }2058        else {2059            <AccountItemCount<T>>::insert(owner.clone(), 1);2060        }20612062        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2063        if list_exists {2064            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2065            let item_contains = list.contains(&item_index.clone());20662067            if !item_contains {2068                list.push(item_index.clone());2069            }20702071            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2072        } else {2073            let mut itm = Vec::new();2074            itm.push(item_index.clone());2075            <AddressTokens<T>>::insert(collection_id, owner, itm);2076            2077        }20782079        Ok(())2080    }20812082    fn remove_token_index(2083        collection_id: u64,2084        item_index: u64,2085        owner: T::AccountId,2086    ) -> DispatchResult {20872088        // update counter2089        <AccountItemCount<T>>::insert(owner.clone(), 2090            <AccountItemCount<T>>::get(owner.clone())2091            .checked_sub(1)2092            .ok_or(Error::<T>::NumOverflow)?);209320942095        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2096        if list_exists {2097            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2098            let item_contains = list.contains(&item_index.clone());20992100            if item_contains {2101                list.retain(|&item| item != item_index);2102                <AddressTokens<T>>::insert(collection_id, owner, list);2103            }2104        }21052106        Ok(())2107    }21082109    fn move_token_index(2110        collection_id: u64,2111        item_index: u64,2112        old_owner: T::AccountId,2113        new_owner: T::AccountId,2114    ) -> DispatchResult {2115        Self::remove_token_index(collection_id, item_index, old_owner)?;2116        Self::add_token_index(collection_id, item_index, new_owner)?;21172118        Ok(())2119    }2120}21212122////////////////////////////////////////////////////////////////////////////////////////////////////2123// Economic models2124// #region21252126/// Fee multiplier.2127pub type Multiplier = FixedU128;21282129type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2130    <T as system::Trait>::AccountId,2131>>::Balance;2132type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2133    <T as system::Trait>::AccountId,2134>>::NegativeImbalance;21352136/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2137/// in the queue.2138#[derive(Encode, Decode, Clone, Eq, PartialEq)]2139pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2140    #[codec(compact)] BalanceOf<T>2141);21422143impl<T: Trait + Send + Sync> sp_std::fmt::Debug2144    for ChargeTransactionPayment<T>2145{2146    #[cfg(feature = "std")]2147    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2148        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2149    }2150    #[cfg(not(feature = "std"))]2151    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2152        Ok(())2153    }2154}21552156impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2157where2158    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2159    BalanceOf<T>: Send + Sync + FixedPointOperand,2160{2161    /// utility constructor. Used only in client/factory code.2162    pub fn from(fee: BalanceOf<T>) -> Self {2163        Self(fee)2164    }21652166    pub fn traditional_fee(2167        len: usize,2168        info: &DispatchInfoOf<T::Call>,2169        tip: BalanceOf<T>,2170    ) -> BalanceOf<T>2171    where2172        T::Call: Dispatchable<Info = DispatchInfo>,2173    {2174        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2175    }21762177    fn withdraw_fee(2178        &self,2179        who: &T::AccountId,2180        call: &T::Call,2181        info: &DispatchInfoOf<T::Call>,2182        len: usize,2183    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2184        let tip = self.0;21852186        // Set fee based on call type. Creating collection costs 1 Unique.2187        // All other transactions have traditional fees so far2188        // let fee = match call.is_sub_type() {2189        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2190        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2191        //                                                 // _ => <BalanceOf<T>>::from(100)2192        // };2193        let fee = Self::traditional_fee(len, info, tip);21942195        // Determine who is paying transaction fee based on ecnomic model2196        // Parse call to extract collection ID and access collection sponsor2197        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2198            Some(Call::create_item(collection_id, _properties, _owner)) => {2199                <Collection<T>>::get(collection_id).sponsor2200            }2201            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2202                let _collection_mode = <Collection<T>>::get(collection_id).mode;22032204                // sponsor timeout2205                let sponsor_transfer = match _collection_mode {2206                    CollectionMode::NFT => {2207                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2208                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2209                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2210                        if block_number >= limit_time {2211                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2212                            true2213                        }2214                        else {2215                            false2216                        }2217                    }2218                    CollectionMode::Fungible(_) => {2219                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2220                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2221                        if basket.iter().any(|i| i.address == _new_owner.clone())2222                        {2223                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2224                            let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2225                            if block_number >= limit_time {2226                                basket.retain(|x| x.address == item.address);2227                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2228                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2229                                true2230                            }2231                            else {2232                                false2233                            }2234                        }2235                        else {2236                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2237                            true2238                        }2239                    }2240                    CollectionMode::ReFungible(_) => {2241                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2242                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2243                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2244                        if block_number >= limit_time {2245                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2246                            true2247                        } else {2248                            false2249                        }2250                    }2251                    _ => {2252                        false2253                    },2254                };22552256                if !sponsor_transfer {2257                    T::AccountId::default()2258                } else {2259                    <Collection<T>>::get(collection_id).sponsor2260                }2261            }22622263            _ => T::AccountId::default(),2264        };22652266        // Sponsor smart contracts2267        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {22682269            // On instantiation: set the contract owner2270            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {22712272                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2273                    code_hash,2274                    &data,2275                    &who,2276                );2277                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());22782279                T::AccountId::default()2280            },22812282            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2283            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {22842285                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());22862287                let mut sponsor_transfer = false;2288                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2289                    let last_tx_block = <ContractSponsorBasket<T>>::get(&called_contract);2290                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2291                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2292                    let limit_time = last_tx_block + rate_limit;22932294                    if block_number >= limit_time {2295                        <ContractSponsorBasket<T>>::insert(called_contract.clone(), block_number);2296                        sponsor_transfer = true;2297                    }2298                } else {2299                    sponsor_transfer = false;2300                }2301               2302                2303                let mut sp = T::AccountId::default();2304                if sponsor_transfer {2305                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2306                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2307                            sp = called_contract;2308                        }2309                    }2310                }23112312                sp2313            },23142315            _ => sponsor,2316        };23172318        let mut who_pays_fee: T::AccountId = sponsor.clone();2319        if sponsor == T::AccountId::default() {2320            who_pays_fee = who.clone();2321        }23222323        // Only mess with balances if fee is not zero.2324        if fee.is_zero() {2325            return Ok((fee, None));2326        }23272328        match <T as transaction_payment::Trait>::Currency::withdraw(2329            &who_pays_fee,2330            fee,2331            if tip.is_zero() {2332                WithdrawReason::TransactionPayment.into()2333            } else {2334                WithdrawReason::TransactionPayment | WithdrawReason::Tip2335            },2336            ExistenceRequirement::KeepAlive,2337        ) {2338            Ok(imbalance) => Ok((fee, Some(imbalance))),2339            Err(_) => Err(InvalidTransaction::Payment.into()),2340        }2341    }2342}234323442345impl<T: Trait + Send + Sync> SignedExtension2346    for ChargeTransactionPayment<T>2347where2348    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2349    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2350{2351    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2352    type AccountId = T::AccountId;2353    type Call = T::Call;2354    type AdditionalSigned = ();2355    type Pre = (2356        BalanceOf<T>,2357        Self::AccountId,2358        Option<NegativeImbalanceOf<T>>,2359        BalanceOf<T>,2360    );2361    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2362        Ok(())2363    }23642365    fn validate(2366        &self,2367        _who: &Self::AccountId,2368        _call: &Self::Call,2369        _info: &DispatchInfoOf<Self::Call>,2370        _len: usize,2371    ) -> TransactionValidity {2372        Ok(ValidTransaction::default())2373    }23742375    fn pre_dispatch(2376        self,2377        who: &Self::AccountId,2378        call: &Self::Call,2379        info: &DispatchInfoOf<Self::Call>,2380        len: usize,2381    ) -> Result<Self::Pre, TransactionValidityError> {2382        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2383        Ok((self.0, who.clone(), imbalance, fee))2384    }23852386    fn post_dispatch(2387        pre: Self::Pre,2388        info: &DispatchInfoOf<Self::Call>,2389        post_info: &PostDispatchInfoOf<Self::Call>,2390        len: usize,2391        _result: &DispatchResult,2392    ) -> Result<(), TransactionValidityError> {2393        let (tip, who, imbalance, fee) = pre;2394        if let Some(payed) = imbalance {2395            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2396                len as u32, info, post_info, tip,2397            );2398            let refund = fee.saturating_sub(actual_fee);2399            let actual_payment =2400                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2401                    &who, refund,2402                ) {2403                    Ok(refund_imbalance) => {2404                        // The refund cannot be larger than the up front payed max weight.2405                        // `PostDispatchInfo::calc_unspent` guards against such a case.2406                        match payed.offset(refund_imbalance) {2407                            Ok(actual_payment) => actual_payment,2408                            Err(_) => return Err(InvalidTransaction::Payment.into()),2409                        }2410                    }2411                    // We do not recreate the account using the refund. The up front payment2412                    // is gone in that case.2413                    Err(_) => payed,2414                };2415            let imbalances = actual_payment.split(tip);2416            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2417                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2418            );2419        }2420        Ok(())2421    }2422}24232424// #endregion