git.delta.rocks / unique-network / refs/commits / 57a41c175b30

difftreelog

source

pallets/nft/src/lib.rs98.4 KiBsourcehistory
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, WithdrawReason,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial,29    },30    IsSubType, StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use pallet_contracts::ContractAddressFor;45use sp_runtime::traits::StaticLookup;4647#[cfg(test)]48mod mock;4950#[cfg(test)]51mod tests;5253mod default_weights;5455pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;56pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;57pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;5859// Structs60// #region6162pub type CollectionId = u32;63pub type TokenId = u32;64pub type DecimalPoints = u8;6566#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]67#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]68pub enum CollectionMode {69    Invalid,70    NFT,71    // decimal points72    Fungible(DecimalPoints),73    // decimal points74    ReFungible(DecimalPoints),75}7677impl Into<u8> for CollectionMode {78    fn into(self) -> u8 {79        match self {80            CollectionMode::Invalid => 0,81            CollectionMode::NFT => 1,82            CollectionMode::Fungible(_) => 2,83            CollectionMode::ReFungible(_) => 3,84        }85    }86}8788#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]89#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]90pub enum AccessMode {91    Normal,92    WhiteList,93}94impl Default for AccessMode {95    fn default() -> Self {96        Self::Normal97    }98}99100impl Default for CollectionMode {101    fn default() -> Self {102        Self::Invalid103    }104}105106#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]107#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]108pub enum SchemaVersion {109    ImageURL,110    Unique,111}112impl Default for SchemaVersion {113    fn default() -> Self {114        Self::ImageURL115    }116}117118#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]119#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]120pub struct Ownership<AccountId> {121    pub owner: AccountId,122    pub fraction: u128,123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct CollectionType<AccountId> {128    pub owner: AccountId,129    pub mode: CollectionMode,130    pub access: AccessMode,131    pub decimal_points: DecimalPoints,132    pub name: Vec<u16>,        // 64 include null escape char133    pub description: Vec<u16>, // 256 include null escape char134    pub token_prefix: Vec<u8>, // 16 include null escape char135    pub mint_mode: bool,136    pub offchain_schema: Vec<u8>,137    pub schema_version: SchemaVersion,138    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender139    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship140    pub limits: CollectionLimits, // Collection private restrictions 141    pub variable_on_chain_schema: Vec<u8>, //142    pub const_on_chain_schema: Vec<u8>, //143}144145#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]146#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]147pub struct NftItemType<AccountId> {148    pub collection: CollectionId,149    pub owner: AccountId,150    pub const_data: Vec<u8>,151    pub variable_data: Vec<u8>,152}153154#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]155#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]156pub struct FungibleItemType<AccountId> {157    pub collection: CollectionId,158    pub owner: AccountId,159    pub value: u128,160}161162#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]163#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]164pub struct ReFungibleItemType<AccountId> {165    pub collection: CollectionId,166    pub owner: Vec<Ownership<AccountId>>,167    pub const_data: Vec<u8>,168    pub variable_data: Vec<u8>,169}170171#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]172#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]173pub struct ApprovePermissions<AccountId> {174    pub approved: AccountId,175    pub amount: u128,176}177178#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]179#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]180pub struct VestingItem<AccountId, Moment> {181    pub sender: AccountId,182    pub recipient: AccountId,183    pub collection_id: CollectionId,184    pub item_id: TokenId,185    pub amount: u64,186    pub vesting_date: Moment,187}188189#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]190#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]191pub struct BasketItem<AccountId, BlockNumber> {192    pub address: AccountId,193    pub start_block: BlockNumber,194}195196#[derive(Encode, Decode, Debug, Clone, PartialEq)]197#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]198pub struct CollectionLimits {199    pub account_token_ownership_limit: u32,200    pub sponsored_data_size: u32,201    pub token_limit: u32,202203    // Timeouts for item types in passed blocks204    pub sponsor_transfer_timeout: u32,205}206207impl Default for CollectionLimits {208    fn default() -> CollectionLimits {209        CollectionLimits { 210            account_token_ownership_limit: 10_000_000, 211            token_limit: u32::max_value(),212            sponsored_data_size: u32::max_value(), 213            sponsor_transfer_timeout: 14400 }214    }215}216217#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]218#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]219pub struct ChainLimits {220    pub collection_numbers_limit: u32,221    pub account_token_ownership_limit: u32,222    pub collections_admins_limit: u64,223    pub custom_data_limit: u32,224225    // Timeouts for item types in passed blocks226    pub nft_sponsor_transfer_timeout: u32,227    pub fungible_sponsor_transfer_timeout: u32,228    pub refungible_sponsor_transfer_timeout: u32,229}230231pub trait WeightInfo {232	fn create_collection() -> Weight;233	fn destroy_collection() -> Weight;234	fn add_to_white_list() -> Weight;235	fn remove_from_white_list() -> Weight;236    fn set_public_access_mode() -> Weight;237    fn set_mint_permission() -> Weight;238    fn change_collection_owner() -> Weight;239    fn add_collection_admin() -> Weight;240    fn remove_collection_admin() -> Weight;241    fn set_collection_sponsor() -> Weight;242    fn confirm_sponsorship() -> Weight;243    fn remove_collection_sponsor() -> Weight;244    fn create_item(s: usize) -> Weight;245    fn burn_item() -> Weight;246    fn transfer() -> Weight;247    fn approve() -> Weight;248    fn transfer_from() -> Weight;249    fn set_offchain_schema() -> Weight;250    fn set_const_on_chain_schema() -> Weight;251    fn set_variable_on_chain_schema() -> Weight;252    fn set_variable_meta_data() -> Weight;253    fn enable_contract_sponsoring() -> Weight;254    fn set_schema_version() -> Weight;255    fn set_chain_limits() -> Weight;256    fn set_contract_sponsoring_rate_limit() -> Weight;257    fn toggle_contract_white_list() -> Weight;258    fn add_to_contract_white_list() -> Weight;259    fn remove_from_contract_white_list() -> Weight;260    fn set_collection_limits() -> Weight;261}262263#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]264#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]265pub struct CreateNftData {266    pub const_data: Vec<u8>,267    pub variable_data: Vec<u8>,268}269270#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]271#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]272pub struct CreateFungibleData {273}274275#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]276#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]277pub struct CreateReFungibleData {278    pub const_data: Vec<u8>,279    pub variable_data: Vec<u8>,280}281282#[derive(Encode, Decode, Debug, Clone, PartialEq)]283#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]284pub enum CreateItemData {285    NFT(CreateNftData),286    Fungible(CreateFungibleData),287    ReFungible(CreateReFungibleData),288}289290impl CreateItemData {291    pub fn len(&self) -> usize {292        let len = match self {293            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),294            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),295            _ => 0296        };297        298        return len;299    }300}301302impl From<CreateNftData> for CreateItemData {303    fn from(item: CreateNftData) -> Self {304        CreateItemData::NFT(item)305    }306}307308impl From<CreateReFungibleData> for CreateItemData {309    fn from(item: CreateReFungibleData) -> Self {310        CreateItemData::ReFungible(item)311    }312}313314impl From<CreateFungibleData> for CreateItemData {315    fn from(item: CreateFungibleData) -> Self {316        CreateItemData::Fungible(item)317    }318}319320321decl_error! {322	/// Error for non-fungible-token module.323	pub enum Error for Module<T: Trait> {324        /// Total collections bound exceeded.325        TotalCollectionsLimitExceeded,326		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.327        CollectionDecimalPointLimitExceeded, 328        /// Collection name can not be longer than 63 char.329        CollectionNameLimitExceeded, 330        /// Collection description can not be longer than 255 char.331        CollectionDescriptionLimitExceeded, 332        /// Token prefix can not be longer than 15 char.333        CollectionTokenPrefixLimitExceeded,334        /// This collection does not exist.335        CollectionNotFound,336        /// Item not exists.337        TokenNotFound,338        /// Arithmetic calculation overflow.339        NumOverflow,       340        /// Account already has admin role.341        AlreadyAdmin,  342        /// You do not own this collection.343        NoPermission,344        /// This address is not set as sponsor, use setCollectionSponsor first.345        ConfirmUnsetSponsorFail,346        /// Collection is not in mint mode.347        PublicMintingNotAllowed,348        /// Sender parameter and item owner must be equal.349        MustBeTokenOwner,350        /// Item balance not enough.351        TokenValueTooLow,352        /// Size of item is too large.353        NftSizeLimitExceeded,354        /// No approve found355        ApproveNotFound,356        /// Requested value more than approved.357        TokenValueNotEnough,358        /// Only approved addresses can call this method.359        ApproveRequired,360        /// Address is not in white list.361        AddresNotInWhiteList,362        /// Number of collection admins bound exceeded.363        CollectionAdminsLimitExceeded,364        /// Owned tokens by a single address bound exceeded.365        AddressOwnershipLimitExceeded,366        /// Length of items properties must be greater than 0.367        EmptyArgument,368        /// const_data exceeded data limit.369        TokenConstDataLimitExceeded,370        /// variable_data exceeded data limit.371        TokenVariableDataLimitExceeded,372        /// Not NFT item data used to mint in NFT collection.373        NotNftDataUsedToMintNftCollectionToken,374        /// Not Fungible item data used to mint in Fungible collection.375        NotFungibleDataUsedToMintFungibleCollectionToken,376        /// Not Re Fungible item data used to mint in Re Fungible collection.377        NotReFungibleDataUsedToMintReFungibleCollectionToken,378        /// Unexpected collection type.379        UnexpectedCollectionType,380        /// Can't store metadata in fungible tokens.381        CantStoreMetadataInFungibleTokens,382        /// Collection token limit exceeded383        CollectionTokenLimitExceeded,384        /// Account token limit exceeded per collection385        AccountTokenLimitExceeded,386        /// Collection limit bounds per collection exceeded387        CollectionLimitBoundsExceeded388	}389}390391pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {392    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;393394    /// Weight information for extrinsics in this pallet.395	type WeightInfo: WeightInfo;396}397398#[cfg(feature = "runtime-benchmarks")]399mod benchmarking;400401// #endregion402403decl_storage! {404    trait Store for Module<T: Trait> as Nft {405406        // Private members407        NextCollectionID: CollectionId;408        CreatedCollectionCount: u32;409        ChainVersion: u64;410        ItemListIndex: map hasher(identity) CollectionId => TokenId;411412        // Chain limits struct413        pub ChainLimit get(fn chain_limit) config(): ChainLimits;414415        // Bound counters416        CollectionCount: u32;417        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;418419        // Basic collections420        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;421        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;422        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;423424        /// Balance owner per collection map425        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;426427        /// second parameter: item id + owner account id428        pub ApprovedList get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;429430        /// Item collections431        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;432        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => FungibleItemType<T::AccountId>;433        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;434435        /// Index list436        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;437438        /// Tokens transfer baskets439        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;440        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Vec<BasketItem<T::AccountId, T::BlockNumber>>;441        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;442443        // Contract Sponsorship and Ownership444        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;445        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;446        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;447        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;448        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 449        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 450    }451    add_extra_genesis {452        build(|config: &GenesisConfig<T>| {453            // Modification of storage454            for (_num, _c) in &config.collection {455                <Module<T>>::init_collection(_c);456            }457458            for (_num, _q, _i) in &config.nft_item_id {459                <Module<T>>::init_nft_token(_i);460            }461462            for (_num, _q, _i) in &config.fungible_item_id {463                <Module<T>>::init_fungible_token(_i);464            }465466            for (_num, _q, _i) in &config.refungible_item_id {467                <Module<T>>::init_refungible_token(_i);468            }469        })470    }471}472473decl_event!(474    pub enum Event<T>475    where476        AccountId = <T as system::Trait>::AccountId,477    {478        /// New collection was created479        /// 480        /// # Arguments481        /// 482        /// * collection_id: Globally unique identifier of newly created collection.483        /// 484        /// * mode: [CollectionMode] converted into u8.485        /// 486        /// * account_id: Collection owner.487        Created(CollectionId, u8, AccountId),488489        /// New item was created.490        /// 491        /// # Arguments492        /// 493        /// * collection_id: Id of the collection where item was created.494        /// 495        /// * item_id: Id of an item. Unique within the collection.496        ItemCreated(CollectionId, TokenId),497498        /// Collection item was burned.499        /// 500        /// # Arguments501        /// 502        /// collection_id.503        /// 504        /// item_id: Identifier of burned NFT.505        ItemDestroyed(CollectionId, TokenId),506    }507);508509decl_module! {510    pub struct Module<T: Trait> for enum Call where origin: T::Origin {511512        fn deposit_event() = default;513        type Error = Error<T>;514515        fn on_initialize(now: T::BlockNumber) -> Weight {516517            if ChainVersion::get() < 2518            {519                let value = NextCollectionID::get();520                CreatedCollectionCount::put(value);521                ChainVersion::put(2);522            }523524            0525        }526527        /// 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.528        /// 529        /// # Permissions530        /// 531        /// * Anyone.532        /// 533        /// # Arguments534        /// 535        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.536        /// 537        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.538        /// 539        /// * token_prefix: UTF-8 string with token prefix.540        /// 541        /// * mode: [CollectionMode] collection type and type dependent data.542        // returns collection ID543        #[weight = T::WeightInfo::create_collection()]544        pub fn create_collection(origin,545                                 collection_name: Vec<u16>,546                                 collection_description: Vec<u16>,547                                 token_prefix: Vec<u8>,548                                 mode: CollectionMode) -> DispatchResult {549550            // Anyone can create a collection551            let who = ensure_signed(origin)?;552553            let decimal_points = match mode {554                CollectionMode::Fungible(points) => points,555                CollectionMode::ReFungible(points) => points,556                _ => 0557            };558559            // bound Total number of collections560            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);561562            // check params563            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);564565            let mut name = collection_name.to_vec();566            name.push(0);567            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);568569            let mut description = collection_description.to_vec();570            description.push(0);571            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);572573            let mut prefix = token_prefix.to_vec();574            prefix.push(0);575            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);576577            // Generate next collection ID578            let next_id = CreatedCollectionCount::get()579                .checked_add(1)580                .ok_or(Error::<T>::NumOverflow)?;581582            // bound counter583            let total = CollectionCount::get()584                .checked_add(1)585                .ok_or(Error::<T>::NumOverflow)?;586587            CreatedCollectionCount::put(next_id);588            CollectionCount::put(total);589590            // Create new collection591            let new_collection = CollectionType {592                owner: who.clone(),593                name: name,594                mode: mode.clone(),595                mint_mode: false,596                access: AccessMode::Normal,597                description: description,598                decimal_points: decimal_points,599                token_prefix: prefix,600                offchain_schema: Vec::new(),601                schema_version: SchemaVersion::ImageURL,602                sponsor: T::AccountId::default(),603                unconfirmed_sponsor: T::AccountId::default(),604                variable_on_chain_schema: Vec::new(),605                const_on_chain_schema: Vec::new(),606                limits: CollectionLimits::default(),607            };608609            // Add new collection to map610            <Collection<T>>::insert(next_id, new_collection);611612            // call event613            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));614615            Ok(())616        }617618        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.619        /// 620        /// # Permissions621        /// 622        /// * Collection Owner.623        /// 624        /// # Arguments625        /// 626        /// * collection_id: collection to destroy.627        #[weight = T::WeightInfo::destroy_collection()]628        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {629630            let sender = ensure_signed(origin)?;631            Self::check_owner_permissions(collection_id, sender)?;632633            <AddressTokens<T>>::remove_prefix(collection_id);634            <ApprovedList<T>>::remove_prefix(collection_id);635            <Balance<T>>::remove_prefix(collection_id);636            <ItemListIndex>::remove(collection_id);637            <AdminList<T>>::remove(collection_id);638            <Collection<T>>::remove(collection_id);639            <WhiteList<T>>::remove_prefix(collection_id);640641            <NftItemList<T>>::remove_prefix(collection_id);642            <FungibleItemList<T>>::remove_prefix(collection_id);643            <ReFungibleItemList<T>>::remove_prefix(collection_id);644645            <NftTransferBasket<T>>::remove_prefix(collection_id);646            <FungibleTransferBasket<T>>::remove_prefix(collection_id);647            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);648649            if CollectionCount::get() > 0650            {651                // bound couter652                let total = CollectionCount::get()653                    .checked_sub(1)654                    .ok_or(Error::<T>::NumOverflow)?;655656                CollectionCount::put(total);657            }658659            Ok(())660        }661662        /// Add an address to white list.663        /// 664        /// # Permissions665        /// 666        /// * Collection Owner667        /// * Collection Admin668        /// 669        /// # Arguments670        /// 671        /// * collection_id.672        /// 673        /// * address.674        #[weight = T::WeightInfo::add_to_white_list()]675        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{676677            let sender = ensure_signed(origin)?;678            Self::check_owner_or_admin_permissions(collection_id, sender)?;679680            <WhiteList<T>>::insert(collection_id, address, true);681            682            Ok(())683        }684685        /// Remove an address from white list.686        /// 687        /// # Permissions688        /// 689        /// * Collection Owner690        /// * Collection Admin691        /// 692        /// # Arguments693        /// 694        /// * collection_id.695        /// 696        /// * address.697        #[weight = T::WeightInfo::remove_from_white_list()]698        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{699700            let sender = ensure_signed(origin)?;701            Self::check_owner_or_admin_permissions(collection_id, sender)?;702703            <WhiteList<T>>::remove(collection_id, address);704705            Ok(())706        }707708        /// Toggle between normal and white list access for the methods with access for `Anyone`.709        /// 710        /// # Permissions711        /// 712        /// * Collection Owner.713        /// 714        /// # Arguments715        /// 716        /// * collection_id.717        /// 718        /// * mode: [AccessMode]719        #[weight = T::WeightInfo::set_public_access_mode()]720        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult721        {722            let sender = ensure_signed(origin)?;723724            Self::check_owner_permissions(collection_id, sender)?;725            let mut target_collection = <Collection<T>>::get(collection_id);726            target_collection.access = mode;727            <Collection<T>>::insert(collection_id, target_collection);728729            Ok(())730        }731732        /// Allows Anyone to create tokens if:733        /// * White List is enabled, and734        /// * Address is added to white list, and735        /// * This method was called with True parameter736        /// 737        /// # Permissions738        /// * Collection Owner739        ///740        /// # Arguments741        /// 742        /// * collection_id.743        /// 744        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.745        #[weight = T::WeightInfo::set_mint_permission()]746        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult747        {748            let sender = ensure_signed(origin)?;749750            Self::check_owner_permissions(collection_id, sender)?;751            let mut target_collection = <Collection<T>>::get(collection_id);752            target_collection.mint_mode = mint_permission;753            <Collection<T>>::insert(collection_id, target_collection);754755            Ok(())756        }757758        /// Change the owner of the collection.759        /// 760        /// # Permissions761        /// 762        /// * Collection Owner.763        /// 764        /// # Arguments765        /// 766        /// * collection_id.767        /// 768        /// * new_owner.769        #[weight = T::WeightInfo::change_collection_owner()]770        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {771772            let sender = ensure_signed(origin)?;773            Self::check_owner_permissions(collection_id, sender)?;774            let mut target_collection = <Collection<T>>::get(collection_id);775            target_collection.owner = new_owner;776            <Collection<T>>::insert(collection_id, target_collection);777778            Ok(())779        }780781        /// Adds an admin of the Collection.782        /// 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. 783        /// 784        /// # Permissions785        /// 786        /// * Collection Owner.787        /// * Collection Admin.788        /// 789        /// # Arguments790        /// 791        /// * collection_id: ID of the Collection to add admin for.792        /// 793        /// * new_admin_id: Address of new admin to add.794        #[weight = T::WeightInfo::add_collection_admin()]795        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {796797            let sender = ensure_signed(origin)?;798            Self::check_owner_or_admin_permissions(collection_id, sender)?;799            let mut admin_arr: Vec<T::AccountId> = Vec::new();800801            if <AdminList<T>>::contains_key(collection_id)802            {803                admin_arr = <AdminList<T>>::get(collection_id);804                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);805            }806807            // Number of collection admins808            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);809810            admin_arr.push(new_admin_id);811            <AdminList<T>>::insert(collection_id, admin_arr);812813            Ok(())814        }815816        /// 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.817        ///818        /// # Permissions819        /// 820        /// * Collection Owner.821        /// * Collection Admin.822        /// 823        /// # Arguments824        /// 825        /// * collection_id: ID of the Collection to remove admin for.826        /// 827        /// * account_id: Address of admin to remove.828        #[weight = T::WeightInfo::remove_collection_admin()]829        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {830831            let sender = ensure_signed(origin)?;832            Self::check_owner_or_admin_permissions(collection_id, sender)?;833834            if <AdminList<T>>::contains_key(collection_id)835            {836                let mut admin_arr = <AdminList<T>>::get(collection_id);837                admin_arr.retain(|i| *i != account_id);838                <AdminList<T>>::insert(collection_id, admin_arr);839            }840841            Ok(())842        }843844        /// # Permissions845        /// 846        /// * Collection Owner847        /// 848        /// # Arguments849        /// 850        /// * collection_id.851        /// 852        /// * new_sponsor.853        #[weight = T::WeightInfo::set_collection_sponsor()]854        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {855856            let sender = ensure_signed(origin)?;857            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);858859            let mut target_collection = <Collection<T>>::get(collection_id);860            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);861862            target_collection.unconfirmed_sponsor = new_sponsor;863            <Collection<T>>::insert(collection_id, target_collection);864865            Ok(())866        }867868        /// # Permissions869        /// 870        /// * Sponsor.871        /// 872        /// # Arguments873        /// 874        /// * collection_id.875        #[weight = T::WeightInfo::confirm_sponsorship()]876        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {877878            let sender = ensure_signed(origin)?;879            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);880881            let mut target_collection = <Collection<T>>::get(collection_id);882            ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);883884            target_collection.sponsor = target_collection.unconfirmed_sponsor;885            target_collection.unconfirmed_sponsor = T::AccountId::default();886            <Collection<T>>::insert(collection_id, target_collection);887888            Ok(())889        }890891        /// Switch back to pay-per-own-transaction model.892        ///893        /// # Permissions894        ///895        /// * Collection owner.896        /// 897        /// # Arguments898        /// 899        /// * collection_id.900        #[weight = T::WeightInfo::remove_collection_sponsor()]901        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {902903            let sender = ensure_signed(origin)?;904            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);905906            let mut target_collection = <Collection<T>>::get(collection_id);907            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);908909            target_collection.sponsor = T::AccountId::default();910            <Collection<T>>::insert(collection_id, target_collection);911912            Ok(())913        }914915        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.916        /// 917        /// # Permissions918        /// 919        /// * Collection Owner.920        /// * Collection Admin.921        /// * Anyone if922        ///     * White List is enabled, and923        ///     * Address is added to white list, and924        ///     * MintPermission is enabled (see SetMintPermission method)925        /// 926        /// # Arguments927        /// 928        /// * collection_id: ID of the collection.929        /// 930        /// * owner: Address, initial owner of the NFT.931        ///932        /// * data: Token data to store on chain.933        // #[weight =934        // (130_000_000 as Weight)935        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))936        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))937        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]938939        #[weight = T::WeightInfo::create_item(data.len())]940        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {941942            let sender = ensure_signed(origin)?;943944            Self::collection_exists(collection_id)?;945946            let target_collection = <Collection<T>>::get(collection_id);947948            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;949            Self::validate_create_item_args(&target_collection, &data)?;950            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;951952            Ok(())953        }954955        /// This method creates multiple instances of NFT Collection created with CreateCollection method.956        /// 957        /// # Permissions958        /// 959        /// * Collection Owner.960        /// * Collection Admin.961        /// * Anyone if962        ///     * White List is enabled, and963        ///     * Address is added to white list, and964        ///     * MintPermission is enabled (see SetMintPermission method)965        /// 966        /// # Arguments967        /// 968        /// * collection_id: ID of the collection.969        /// 970        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].971        /// 972        /// * owner: Address, initial owner of the NFT.973        #[weight = T::WeightInfo::create_item(items_data.into_iter()974                               .map(|data| { data.len() })975                               .sum())]976        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {977978            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);979            let sender = ensure_signed(origin)?;980981            Self::collection_exists(collection_id)?;982            let target_collection = <Collection<T>>::get(collection_id);983984            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;985986            for data in &items_data {987                Self::validate_create_item_args(&target_collection, data)?;988            }989            for data in &items_data {990                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;991            }992993            Ok(())994        }995996        /// Destroys a concrete instance of NFT.997        /// 998        /// # Permissions999        /// 1000        /// * Collection Owner.1001        /// * Collection Admin.1002        /// * Current NFT Owner.1003        /// 1004        /// # Arguments1005        /// 1006        /// * collection_id: ID of the collection.1007        /// 1008        /// * item_id: ID of NFT to burn.1009        #[weight = T::WeightInfo::burn_item()]1010        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {10111012            let sender = ensure_signed(origin)?;1013            Self::collection_exists(collection_id)?;10141015            // Transfer permissions check1016            let target_collection = <Collection<T>>::get(collection_id);1017            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1018                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1019                Error::<T>::NoPermission);10201021            if target_collection.access == AccessMode::WhiteList {1022                Self::check_white_list(collection_id, &sender)?;1023            }10241025            match target_collection.mode1026            {1027                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1028                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,1029                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1030                _ => ()1031            };10321033            // call event1034            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10351036            Ok(())1037        }10381039        /// Change ownership of the token.1040        /// 1041        /// # Permissions1042        /// 1043        /// * Collection Owner1044        /// * Collection Admin1045        /// * Current NFT owner1046        ///1047        /// # Arguments1048        /// 1049        /// * recipient: Address of token recipient.1050        /// 1051        /// * collection_id.1052        /// 1053        /// * item_id: ID of the item1054        ///     * Non-Fungible Mode: Required.1055        ///     * Fungible Mode: Ignored.1056        ///     * Re-Fungible Mode: Required.1057        /// 1058        /// * value: Amount to transfer.1059        ///     * Non-Fungible Mode: Ignored1060        ///     * Fungible Mode: Must specify transferred amount1061        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1062        #[weight = T::WeightInfo::transfer()]1063        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10641065            let sender = ensure_signed(origin)?;1066            let target_collection = <Collection<T>>::get(collection_id);10671068            // Limits check1069            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;10701071            // Transfer permissions check1072            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1073                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1074                Error::<T>::NoPermission);10751076            if target_collection.access == AccessMode::WhiteList {1077                Self::check_white_list(collection_id, &sender)?;1078                Self::check_white_list(collection_id, &recipient)?;1079            }10801081            match target_collection.mode1082            {1083                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1084                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1085                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1086                _ => ()1087            };10881089            Ok(())1090        }10911092        /// Set, change, or remove approved address to transfer the ownership of the NFT.1093        /// 1094        /// # Permissions1095        /// 1096        /// * Collection Owner1097        /// * Collection Admin1098        /// * Current NFT owner1099        /// 1100        /// # Arguments1101        /// 1102        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1103        /// 1104        /// * collection_id.1105        /// 1106        /// * item_id: ID of the item.1107        #[weight = T::WeightInfo::approve()]1108        pub fn approve(origin, approved: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {11091110            let sender = ensure_signed(origin)?;11111112            // Transfer permissions check1113            let target_collection = <Collection<T>>::get(collection_id);1114            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1115                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1116                Error::<T>::NoPermission);11171118            if target_collection.access == AccessMode::WhiteList {1119                Self::check_white_list(collection_id, &sender)?;1120                Self::check_white_list(collection_id, &approved)?;1121            }11221123            // amount param stub1124            let amount = 100000000;11251126            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1127            if list_exists {11281129                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1130                let item_contains = list.iter().any(|i| i.approved == approved);11311132                if !item_contains {1133                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1134                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1135                }1136            } else {11371138                let mut list = Vec::new();1139                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1140                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1141            }11421143            Ok(())1144        }1145        1146        /// 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.1147        /// 1148        /// # Permissions1149        /// * Collection Owner1150        /// * Collection Admin1151        /// * Current NFT owner1152        /// * Address approved by current NFT owner1153        /// 1154        /// # Arguments1155        /// 1156        /// * from: Address that owns token.1157        /// 1158        /// * recipient: Address of token recipient.1159        /// 1160        /// * collection_id.1161        /// 1162        /// * item_id: ID of the item.1163        /// 1164        /// * value: Amount to transfer.1165        #[weight = T::WeightInfo::transfer_from()]1166        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11671168            let sender = ensure_signed(origin)?;1169            let mut appoved_transfer = false;11701171            // Check approve1172            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1173                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1174                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1175                if opt_item.is_some()1176                {1177                    appoved_transfer = true;1178                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1179                }1180            }11811182            let target_collection = <Collection<T>>::get(collection_id);11831184            // Limits check1185            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11861187            // Transfer permissions check         1188            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1189            Error::<T>::NoPermission);11901191            if target_collection.access == AccessMode::WhiteList {1192                Self::check_white_list(collection_id, &sender)?;1193                Self::check_white_list(collection_id, &recipient)?;1194            }11951196            // remove approve1197            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1198                .into_iter().filter(|i| i.approved != sender.clone()).collect();1199            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);120012011202            match target_collection.mode1203            {1204                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1205                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1206                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1207                _ => ()1208            };12091210            Ok(())1211        }12121213        #[weight = 0]1214        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {12151216            // let no_perm_mes = "You do not have permissions to modify this collection";1217            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1218            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1219            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);12201221            // // on_nft_received  call12221223            // Self::transfer(origin, collection_id, item_id, new_owner)?;12241225            Ok(())1226        }12271228        /// Set off-chain data schema.1229        /// 1230        /// # Permissions1231        /// 1232        /// * Collection Owner1233        /// * Collection Admin1234        /// 1235        /// # Arguments1236        /// 1237        /// * collection_id.1238        /// 1239        /// * schema: String representing the offchain data schema.1240        #[weight = T::WeightInfo::set_variable_meta_data()]1241        pub fn set_variable_meta_data (1242            origin,1243            collection_id: CollectionId,1244            item_id: TokenId,1245            data: Vec<u8>1246        ) -> DispatchResult {1247            let sender = ensure_signed(origin)?;1248            1249            Self::collection_exists(collection_id)?;1250            1251            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12521253            // Modify permissions check1254            let target_collection = <Collection<T>>::get(collection_id);1255            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1256                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1257                Error::<T>::NoPermission);12581259            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12601261            match target_collection.mode1262            {1263                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1264                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1265                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1266                _ => fail!(Error::<T>::UnexpectedCollectionType)1267            };12681269            Ok(())1270        }1271 1272        /// Set schema standard1273        /// ImageURL1274        /// Unique1275        /// 1276        /// # Permissions1277        /// 1278        /// * Collection Owner1279        /// * Collection Admin1280        /// 1281        /// # Arguments1282        /// 1283        /// * collection_id.1284        /// 1285        /// * schema: SchemaVersion: enum1286        #[weight = T::WeightInfo::set_schema_version()]1287        pub fn set_schema_version(1288            origin,1289            collection_id: CollectionId,1290            version: SchemaVersion1291        ) -> DispatchResult {1292            let sender = ensure_signed(origin)?;1293            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1294            let mut target_collection = <Collection<T>>::get(collection_id);1295            target_collection.schema_version = version;1296            <Collection<T>>::insert(collection_id, target_collection);12971298            Ok(())1299        }13001301        /// Set off-chain data schema.1302        /// 1303        /// # Permissions1304        /// 1305        /// * Collection Owner1306        /// * Collection Admin1307        /// 1308        /// # Arguments1309        /// 1310        /// * collection_id.1311        /// 1312        /// * schema: String representing the offchain data schema.1313        #[weight = T::WeightInfo::set_offchain_schema()]1314        pub fn set_offchain_schema(1315            origin,1316            collection_id: CollectionId,1317            schema: Vec<u8>1318        ) -> DispatchResult {1319            let sender = ensure_signed(origin)?;1320            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13211322            let mut target_collection = <Collection<T>>::get(collection_id);1323            target_collection.offchain_schema = schema;1324            <Collection<T>>::insert(collection_id, target_collection);13251326            Ok(())1327        }13281329        /// Set const on-chain data schema.1330        /// 1331        /// # Permissions1332        /// 1333        /// * Collection Owner1334        /// * Collection Admin1335        /// 1336        /// # Arguments1337        /// 1338        /// * collection_id.1339        /// 1340        /// * schema: String representing the const on-chain data schema.1341        #[weight = T::WeightInfo::set_const_on_chain_schema()]1342        pub fn set_const_on_chain_schema (1343            origin,1344            collection_id: CollectionId,1345            schema: Vec<u8>1346        ) -> DispatchResult {1347            let sender = ensure_signed(origin)?;1348            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13491350            let mut target_collection = <Collection<T>>::get(collection_id);1351            target_collection.const_on_chain_schema = schema;1352            <Collection<T>>::insert(collection_id, target_collection);13531354            Ok(())1355        }13561357        /// Set variable on-chain data schema.1358        /// 1359        /// # Permissions1360        /// 1361        /// * Collection Owner1362        /// * Collection Admin1363        /// 1364        /// # Arguments1365        /// 1366        /// * collection_id.1367        /// 1368        /// * schema: String representing the variable on-chain data schema.1369        #[weight = T::WeightInfo::set_const_on_chain_schema()]1370        pub fn set_variable_on_chain_schema (1371            origin,1372            collection_id: CollectionId,1373            schema: Vec<u8>1374        ) -> DispatchResult {1375            let sender = ensure_signed(origin)?;1376            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13771378            let mut target_collection = <Collection<T>>::get(collection_id);1379            target_collection.variable_on_chain_schema = schema;1380            <Collection<T>>::insert(collection_id, target_collection);13811382            Ok(())1383        }13841385        // Sudo permissions function1386        #[weight = T::WeightInfo::set_chain_limits()]1387        pub fn set_chain_limits(1388            origin,1389            limits: ChainLimits1390        ) -> DispatchResult {13911392            #[cfg(not(feature = "runtime-benchmarks"))]1393            ensure_root(origin)?;13941395            <ChainLimit>::put(limits);1396            Ok(())1397        }13981399        /// Enable smart contract self-sponsoring.1400        /// 1401        /// # Permissions1402        /// 1403        /// * Contract Owner1404        /// 1405        /// # Arguments1406        /// 1407        /// * contract address1408        /// * enable flag1409        /// 1410        #[weight = T::WeightInfo::enable_contract_sponsoring()]1411        pub fn enable_contract_sponsoring(1412            origin,1413            contract_address: T::AccountId,1414            enable: bool1415        ) -> DispatchResult {14161417            let sender = ensure_signed(origin)?;14181419            #[cfg(feature = "runtime-benchmarks")]1420            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14211422            Self::ensure_contract_owned(sender, &contract_address)?;14231424            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1425            Ok(())1426        }14271428        /// Set the rate limit for contract sponsoring to specified number of blocks.1429        /// 1430        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1431        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1432        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1433        /// from contract endowment if there are at least B blocks between such transactions. 1434        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1435        /// 1436        /// # Permissions1437        /// 1438        /// * Contract Owner1439        /// 1440        /// # Arguments1441        /// 1442        /// -`contract_address`: Address of the contract to sponsor1443        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1444        /// 1445        #[weight = T::WeightInfo::set_contract_sponsoring_rate_limit()]1446        pub fn set_contract_sponsoring_rate_limit(1447            origin,1448            contract_address: T::AccountId,1449            rate_limit: T::BlockNumber1450        ) -> DispatchResult {1451            let sender = ensure_signed(origin)?;14521453            #[cfg(feature = "runtime-benchmarks")]1454            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14551456            Self::ensure_contract_owned(sender, &contract_address)?;1457            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1458            Ok(())1459        }14601461        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1462        /// 1463        /// # Permissions1464        /// 1465        /// * Address that deployed smart contract.1466        /// 1467        /// # Arguments1468        /// 1469        /// -`contract_address`: Address of the contract.1470        /// 1471        /// - `enable`: .  1472        #[weight = T::WeightInfo::toggle_contract_white_list()]1473        pub fn toggle_contract_white_list(1474            origin,1475            contract_address: T::AccountId,1476            enable: bool1477        ) -> DispatchResult {1478            let sender = ensure_signed(origin)?;14791480            #[cfg(feature = "runtime-benchmarks")]1481            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14821483            Self::ensure_contract_owned(sender, &contract_address)?;1484            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1485            Ok(())1486        }1487        1488        /// Add an address to smart contract white list.1489        /// 1490        /// # Permissions1491        /// 1492        /// * Address that deployed smart contract.1493        /// 1494        /// # Arguments1495        /// 1496        /// -`contract_address`: Address of the contract.1497        ///1498        /// -`account_address`: Address to add.1499        #[weight = T::WeightInfo::add_to_contract_white_list()]1500        pub fn add_to_contract_white_list(1501            origin,1502            contract_address: T::AccountId,1503            account_address: T::AccountId1504        ) -> DispatchResult {1505            let sender = ensure_signed(origin)?;15061507            #[cfg(feature = "runtime-benchmarks")]1508            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1509            1510            Self::ensure_contract_owned(sender, &contract_address)?;      1511            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1512            Ok(())1513        }15141515        /// Remove an address from smart contract white list.1516        /// 1517        /// # Permissions1518        /// 1519        /// * Address that deployed smart contract.1520        /// 1521        /// # Arguments1522        /// 1523        /// -`contract_address`: Address of the contract.1524        ///1525        /// -`account_address`: Address to remove.1526        #[weight = T::WeightInfo::remove_from_contract_white_list()]1527        pub fn remove_from_contract_white_list(1528            origin,1529            contract_address: T::AccountId,1530            account_address: T::AccountId1531        ) -> DispatchResult {1532            let sender = ensure_signed(origin)?;15331534            #[cfg(feature = "runtime-benchmarks")]1535            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15361537            Self::ensure_contract_owned(sender, &contract_address)?;1538            <ContractWhiteList<T>>::remove(contract_address, account_address);1539            Ok(())1540        }15411542        #[weight = T::WeightInfo::set_collection_limits()]1543        pub fn set_collection_limits(1544            origin,1545            collection_id: u32,1546            limits: CollectionLimits,1547        ) -> DispatchResult {1548            let sender = ensure_signed(origin)?;1549            Self::check_owner_permissions(collection_id, sender.clone())?;1550            let mut target_collection = <Collection<T>>::get(collection_id);1551            let chain_limits = ChainLimit::get();1552            let climits = target_collection.limits;15531554            // collection bounds1555            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1556                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1557                Error::<T>::CollectionLimitBoundsExceeded);15581559            // token_limit   check  prev1560            ensure!(climits.token_limit > limits.token_limit && 1561                limits.token_limit <= chain_limits.account_token_ownership_limit, 1562                Error::<T>::AccountTokenLimitExceeded);15631564            target_collection.limits = limits;1565            <Collection<T>>::insert(collection_id, target_collection);15661567            Ok(())1568        } 1569    }1570}15711572impl<T: Trait> Module<T> {15731574    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15751576        // check token limit and account token limit1577        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1578        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1579        1580        Ok(())1581    }15821583    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15841585        // check token limit and account token limit1586        let total_items: u32 = ItemListIndex::get(collection_id);1587        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1588        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1589        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15901591        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1592            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1593            Self::check_white_list(collection_id, owner)?;1594            Self::check_white_list(collection_id, sender)?;1595        }15961597        Ok(())1598    }15991600    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1601        match target_collection.mode1602        {1603            CollectionMode::NFT => {1604                if let CreateItemData::NFT(data) = data {1605                    // check sizes1606                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1607                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1608                } else {1609                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1610                }1611            },1612            CollectionMode::Fungible(_) => {1613                if let CreateItemData::Fungible(_) = data {1614                } else {1615                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1616                }1617            },1618            CollectionMode::ReFungible(_) => {1619                if let CreateItemData::ReFungible(data) = data {16201621                    // check sizes1622                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1623                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1624                } else {1625                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1626                }1627            },1628            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1629        };16301631        Ok(())1632    }16331634    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1635        match data1636        {1637            CreateItemData::NFT(data) => {1638                let item = NftItemType {1639                    collection: collection_id,1640                    owner,1641                    const_data: data.const_data,1642                    variable_data: data.variable_data1643                };16441645                Self::add_nft_item(item)?;1646            },1647            CreateItemData::Fungible(_) => {1648                let item = FungibleItemType {1649                    collection: collection_id,1650                    owner,1651                    value: (10 as u128).pow(collection.decimal_points as u32)1652                };16531654                Self::add_fungible_item(item)?;1655            },1656            CreateItemData::ReFungible(data) => {1657                let mut owner_list = Vec::new();1658                let value = (10 as u128).pow(collection.decimal_points as u32);1659                owner_list.push(Ownership {owner: owner.clone(), fraction: value});16601661                let item = ReFungibleItemType {1662                    collection: collection_id,1663                    owner: owner_list,1664                    const_data: data.const_data,1665                    variable_data: data.variable_data1666                };16671668                Self::add_refungible_item(item)?;1669            }1670        };16711672        // call event1673        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16741675        Ok(())1676    }16771678    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1679        let current_index = <ItemListIndex>::get(item.collection)1680            .checked_add(1)1681            .ok_or(Error::<T>::NumOverflow)?;1682        let itemcopy = item.clone();1683        let owner = item.owner.clone();16841685        Self::add_token_index(item.collection, current_index, owner.clone())?;16861687        <ItemListIndex>::insert(item.collection, current_index);1688        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);16891690        // Add current block1691        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1692        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1693        1694        // Update balance1695        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1696            .checked_add(item.value)1697            .ok_or(Error::<T>::NumOverflow)?;1698        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);16991700        Ok(())1701    }17021703    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1704        let current_index = <ItemListIndex>::get(item.collection)1705            .checked_add(1)1706            .ok_or(Error::<T>::NumOverflow)?;1707        let itemcopy = item.clone();17081709        let value = item.owner.first().unwrap().fraction;1710        let owner = item.owner.first().unwrap().owner.clone();17111712        Self::add_token_index(item.collection, current_index, owner.clone())?;17131714        <ItemListIndex>::insert(item.collection, current_index);1715        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);17161717        // Add current block1718        let block_number: T::BlockNumber = 0.into();1719        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);17201721        // Update balance1722        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1723            .checked_add(value)1724            .ok_or(Error::<T>::NumOverflow)?;1725        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);17261727        Ok(())1728    }17291730    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1731        let current_index = <ItemListIndex>::get(item.collection)1732            .checked_add(1)1733            .ok_or(Error::<T>::NumOverflow)?;17341735        let item_owner = item.owner.clone();1736        let collection_id = item.collection.clone();1737        Self::add_token_index(collection_id, current_index, item.owner.clone())?;17381739        <ItemListIndex>::insert(collection_id, current_index);1740        <NftItemList<T>>::insert(collection_id, current_index, item);17411742        // Add current block1743        let block_number: T::BlockNumber = 0.into();1744        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);17451746        // Update balance1747        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1748            .checked_add(1)1749            .ok_or(Error::<T>::NumOverflow)?;1750        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17511752        Ok(())1753    }17541755    fn burn_refungible_item(1756        collection_id: CollectionId,1757        item_id: TokenId,1758        owner: T::AccountId,1759    ) -> DispatchResult {1760        ensure!(1761            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1762            Error::<T>::TokenNotFound1763        );1764        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1765        let item = collection1766            .owner1767            .iter()1768            .filter(|&i| i.owner == owner)1769            .next()1770            .unwrap();1771        Self::remove_token_index(collection_id, item_id, owner.clone())?;17721773        // remove approve list1774        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));17751776        // update balance1777        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1778            .checked_sub(item.fraction)1779            .ok_or(Error::<T>::NumOverflow)?;1780        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17811782        <ReFungibleItemList<T>>::remove(collection_id, item_id);17831784        Ok(())1785    }17861787    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1788        ensure!(1789            <NftItemList<T>>::contains_key(collection_id, item_id),1790            Error::<T>::TokenNotFound1791        );1792        let item = <NftItemList<T>>::get(collection_id, item_id);1793        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17941795        // remove approve list1796        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));17971798        // update balance1799        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1800            .checked_sub(1)1801            .ok_or(Error::<T>::NumOverflow)?;1802        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1803        <NftItemList<T>>::remove(collection_id, item_id);18041805        Ok(())1806    }18071808    fn burn_fungible_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1809        ensure!(1810            <FungibleItemList<T>>::contains_key(collection_id, item_id),1811            Error::<T>::TokenNotFound1812        );1813        let item = <FungibleItemList<T>>::get(collection_id, item_id);1814        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;18151816        // remove approve list1817        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));18181819        // update balance1820        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1821            .checked_sub(item.value)1822            .ok_or(Error::<T>::NumOverflow)?;1823        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);18241825        <FungibleItemList<T>>::remove(collection_id, item_id);18261827        Ok(())1828    }18291830    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1831        ensure!(1832            <Collection<T>>::contains_key(collection_id),1833            Error::<T>::CollectionNotFound1834        );1835        Ok(())1836    }18371838    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1839        Self::collection_exists(collection_id)?;18401841        let target_collection = <Collection<T>>::get(collection_id);1842        ensure!(1843            subject == target_collection.owner,1844            Error::<T>::NoPermission1845        );18461847        Ok(())1848    }18491850    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1851        let target_collection = <Collection<T>>::get(collection_id);1852        let mut result: bool = subject == target_collection.owner;1853        let exists = <AdminList<T>>::contains_key(collection_id);18541855        if !result & exists {1856            if <AdminList<T>>::get(collection_id).contains(&subject) {1857                result = true1858            }1859        }18601861        result1862    }18631864    fn check_owner_or_admin_permissions(1865        collection_id: CollectionId,1866        subject: T::AccountId,1867    ) -> DispatchResult {1868        Self::collection_exists(collection_id)?;1869        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18701871        ensure!(1872            result,1873            Error::<T>::NoPermission1874        );1875        Ok(())1876    }18771878    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1879        let target_collection = <Collection<T>>::get(collection_id);18801881        match target_collection.mode {1882            CollectionMode::NFT => {1883                <NftItemList<T>>::get(collection_id, item_id).owner == subject1884            }1885            CollectionMode::Fungible(_) => {1886                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1887            }1888            CollectionMode::ReFungible(_) => {1889                <ReFungibleItemList<T>>::get(collection_id, item_id)1890                    .owner1891                    .iter()1892                    .any(|i| i.owner == subject)1893            }1894            CollectionMode::Invalid => false,1895        }1896    }18971898    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1899        let mes = Error::<T>::AddresNotInWhiteList;1900        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);19011902        Ok(())1903    }19041905    fn transfer_fungible(1906        collection_id: CollectionId,1907        item_id: TokenId,1908        value: u128,1909        owner: T::AccountId,1910        new_owner: T::AccountId,1911    ) -> DispatchResult {1912        ensure!(1913            <FungibleItemList<T>>::contains_key(collection_id, item_id),1914            Error::<T>::TokenNotFound1915        );19161917        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1918        let amount = full_item.value;19191920        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19211922        // update balance1923        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1924            .checked_sub(value)1925            .ok_or(Error::<T>::NumOverflow)?;1926        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);19271928        let mut new_owner_account_id = 0;1929        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1930        if new_owner_items.len() > 0 {1931            new_owner_account_id = new_owner_items[0];1932        }19331934        // transfer1935        if amount == value && new_owner_account_id == 0 {1936            // change owner1937            // new owner do not have account1938            let mut new_full_item = full_item.clone();1939            new_full_item.owner = new_owner.clone();1940            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19411942            // update balance1943            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1944                .checked_add(value)1945                .ok_or(Error::<T>::NumOverflow)?;1946            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19471948            // update index collection1949            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1950        } else {1951            let mut new_full_item = full_item.clone();1952            new_full_item.value -= value;19531954            // separate amount1955            if new_owner_account_id > 0 {1956                // new owner has account1957                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1958                item.value += value;19591960                // update balance1961                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1962                    .checked_add(value)1963                    .ok_or(Error::<T>::NumOverflow)?;1964                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19651966                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1967            } else {1968                // new owner do not have account1969                let item = FungibleItemType {1970                    collection: collection_id,1971                    owner: new_owner.clone(),1972                    value1973                };19741975                Self::add_fungible_item(item)?;1976            }19771978            if amount == value {1979                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;19801981                // remove approve list1982                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1983                <FungibleItemList<T>>::remove(collection_id, item_id);1984            }19851986            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1987        }19881989        Ok(())1990    }19911992    fn transfer_refungible(1993        collection_id: CollectionId,1994        item_id: TokenId,1995        value: u128,1996        owner: T::AccountId,1997        new_owner: T::AccountId,1998    ) -> DispatchResult {1999        ensure!(2000            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2001            Error::<T>::TokenNotFound2002        );20032004        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);2005        let item = full_item2006            .owner2007            .iter()2008            .filter(|i| i.owner == owner)2009            .next()2010            .ok_or(Error::<T>::NumOverflow)?;2011        let amount = item.fraction;20122013        ensure!(amount >= value, Error::<T>::TokenValueTooLow);20142015        // update balance2016        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2017            .checked_sub(value)2018            .ok_or(Error::<T>::NumOverflow)?;2019        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20202021        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())2022            .checked_add(value)2023            .ok_or(Error::<T>::NumOverflow)?;2024        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);20252026        let old_owner = item.owner.clone();2027        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20282029        // transfer2030        if amount == value && !new_owner_has_account {2031            // change owner2032            // new owner do not have account2033            let mut new_full_item = full_item.clone();2034            new_full_item2035                .owner2036                .iter_mut()2037                .find(|i| i.owner == owner)2038                .unwrap()2039                .owner = new_owner.clone();2040            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20412042            // update index collection2043            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;2044        } else {2045            let mut new_full_item = full_item.clone();2046            new_full_item2047                .owner2048                .iter_mut()2049                .find(|i| i.owner == owner)2050                .unwrap()2051                .fraction -= value;20522053            // separate amount2054            if new_owner_has_account {2055                // new owner has account2056                new_full_item2057                    .owner2058                    .iter_mut()2059                    .find(|i| i.owner == new_owner)2060                    .unwrap()2061                    .fraction += value;2062            } else {2063                // new owner do not have account2064                new_full_item.owner.push(Ownership {2065                    owner: new_owner.clone(),2066                    fraction: value,2067                });2068                Self::add_token_index(collection_id, item_id, new_owner.clone())?;2069            }20702071            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2072        }20732074        Ok(())2075    }20762077    fn transfer_nft(2078        collection_id: CollectionId,2079        item_id: TokenId,2080        sender: T::AccountId,2081        new_owner: T::AccountId,2082    ) -> DispatchResult {2083        ensure!(2084            <NftItemList<T>>::contains_key(collection_id, item_id),2085            Error::<T>::TokenNotFound2086        );20872088        let mut item = <NftItemList<T>>::get(collection_id, item_id);20892090        ensure!(2091            sender == item.owner,2092            Error::<T>::MustBeTokenOwner2093        );20942095        // update balance2096        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2097            .checked_sub(1)2098            .ok_or(Error::<T>::NumOverflow)?;2099        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);21002101        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())2102            .checked_add(1)2103            .ok_or(Error::<T>::NumOverflow)?;2104        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);21052106        // change owner2107        let old_owner = item.owner.clone();2108        item.owner = new_owner.clone();2109        <NftItemList<T>>::insert(collection_id, item_id, item);21102111        // update index collection2112        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;21132114        // reset approved list2115        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));2116        Ok(())2117    }2118    2119    fn item_exists(2120        collection_id: CollectionId,2121        item_id: TokenId,2122        mode: &CollectionMode2123    ) -> DispatchResult {2124        match mode {2125            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2126            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2127            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2128            _ => ()2129        };2130        2131        Ok(())2132    }21332134    fn set_re_fungible_variable_data(2135        collection_id: CollectionId,2136        item_id: TokenId,2137        data: Vec<u8>2138    ) -> DispatchResult {2139        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);21402141        item.variable_data = data;21422143        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21442145        Ok(())2146    }21472148    fn set_nft_variable_data(2149        collection_id: CollectionId,2150        item_id: TokenId,2151        data: Vec<u8>2152    ) -> DispatchResult {2153        let mut item = <NftItemList<T>>::get(collection_id, item_id);2154        2155        item.variable_data = data;21562157        <NftItemList<T>>::insert(collection_id, item_id, item);2158        2159        Ok(())2160    }21612162    fn init_collection(item: &CollectionType<T::AccountId>) {2163        // check params2164        assert!(2165            item.decimal_points <= MAX_DECIMAL_POINTS,2166            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2167        );2168        assert!(2169            item.name.len() <= 64,2170            "Collection name can not be longer than 63 char"2171        );2172        assert!(2173            item.name.len() <= 256,2174            "Collection description can not be longer than 255 char"2175        );2176        assert!(2177            item.token_prefix.len() <= 16,2178            "Token prefix can not be longer than 15 char"2179        );21802181        // Generate next collection ID2182        let next_id = CreatedCollectionCount::get()2183            .checked_add(1)2184            .unwrap();21852186        CreatedCollectionCount::put(next_id);2187    }21882189    fn init_nft_token(item: &NftItemType<T::AccountId>) {2190        let current_index = <ItemListIndex>::get(item.collection)2191            .checked_add(1)2192            .unwrap();21932194        let item_owner = item.owner.clone();2195        let collection_id = item.collection.clone();2196        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();21972198        <ItemListIndex>::insert(collection_id, current_index);21992200        // Update balance2201        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2202            .checked_add(1)2203            .unwrap();2204        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2205    }22062207    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2208        let current_index = <ItemListIndex>::get(item.collection)2209            .checked_add(1)2210            .unwrap();2211        let owner = item.owner.clone();22122213        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();22142215        <ItemListIndex>::insert(item.collection, current_index);22162217        // Update balance2218        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2219            .checked_add(item.value)2220            .unwrap();2221        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2222    }22232224    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2225        let current_index = <ItemListIndex>::get(item.collection)2226            .checked_add(1)2227            .unwrap();22282229        let value = item.owner.first().unwrap().fraction;2230        let owner = item.owner.first().unwrap().owner.clone();22312232        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();22332234        <ItemListIndex>::insert(item.collection, current_index);22352236        // Update balance2237        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2238            .checked_add(value)2239            .unwrap();2240        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2241    }22422243    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {22442245        // add to account limit2246        if <AccountItemCount<T>>::contains_key(owner.clone()) {22472248            // bound Owned tokens by a single address2249            let count = <AccountItemCount<T>>::get(owner.clone());2250            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);22512252            <AccountItemCount<T>>::insert(owner.clone(), count2253                .checked_add(1)2254                .ok_or(Error::<T>::NumOverflow)?);2255        }2256        else {2257            <AccountItemCount<T>>::insert(owner.clone(), 1);2258        }22592260        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2261        if list_exists {2262            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2263            let item_contains = list.contains(&item_index.clone());22642265            if !item_contains {2266                list.push(item_index.clone());2267            }22682269            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2270        } else {2271            let mut itm = Vec::new();2272            itm.push(item_index.clone());2273            <AddressTokens<T>>::insert(collection_id, owner, itm);2274            2275        }22762277        Ok(())2278    }22792280    fn remove_token_index(2281        collection_id: CollectionId,2282        item_index: TokenId,2283        owner: T::AccountId,2284    ) -> DispatchResult {22852286        // update counter2287        <AccountItemCount<T>>::insert(owner.clone(), 2288            <AccountItemCount<T>>::get(owner.clone())2289            .checked_sub(1)2290            .ok_or(Error::<T>::NumOverflow)?);229122922293        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2294        if list_exists {2295            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2296            let item_contains = list.contains(&item_index.clone());22972298            if item_contains {2299                list.retain(|&item| item != item_index);2300                <AddressTokens<T>>::insert(collection_id, owner, list);2301            }2302        }23032304        Ok(())2305    }23062307    fn move_token_index(2308        collection_id: CollectionId,2309        item_index: TokenId,2310        old_owner: T::AccountId,2311        new_owner: T::AccountId,2312    ) -> DispatchResult {2313        Self::remove_token_index(collection_id, item_index, old_owner)?;2314        Self::add_token_index(collection_id, item_index, new_owner)?;23152316        Ok(())2317    }2318    2319    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2320        if <ContractOwner<T>>::contains_key(contract.clone()) {2321            let owner = <ContractOwner<T>>::get(contract);2322            ensure!(account == owner, Error::<T>::NoPermission);2323        } else {2324            fail!(Error::<T>::NoPermission);2325        }23262327        Ok(())2328    }2329}23302331////////////////////////////////////////////////////////////////////////////////////////////////////2332// Economic models2333// #region23342335/// Fee multiplier.2336pub type Multiplier = FixedU128;23372338type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2339    <T as system::Trait>::AccountId,2340>>::Balance;2341type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2342    <T as system::Trait>::AccountId,2343>>::NegativeImbalance;23442345/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2346/// in the queue.2347#[derive(Encode, Decode, Clone, Eq, PartialEq)]2348pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2349    #[codec(compact)] BalanceOf<T>2350);23512352impl<T: Trait + Send + Sync> sp_std::fmt::Debug2353    for ChargeTransactionPayment<T>2354{2355    #[cfg(feature = "std")]2356    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2357        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2358    }2359    #[cfg(not(feature = "std"))]2360    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2361        Ok(())2362    }2363}23642365impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2366where2367    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2368    BalanceOf<T>: Send + Sync + FixedPointOperand,2369{2370    /// utility constructor. Used only in client/factory code.2371    pub fn from(fee: BalanceOf<T>) -> Self {2372        Self(fee)2373    }23742375    pub fn traditional_fee(2376        len: usize,2377        info: &DispatchInfoOf<T::Call>,2378        tip: BalanceOf<T>,2379    ) -> BalanceOf<T>2380    where2381        T::Call: Dispatchable<Info = DispatchInfo>,2382    {2383        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2384    }23852386	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2387		let weight_saturation = T::MaximumBlockWeight::get() / info.weight.max(1);2388		let len_saturation = T::MaximumBlockLength::get() as u64 / (len as u64).max(1);2389		let coefficient: BalanceOf<T> = weight_saturation.min(len_saturation).saturated_into::<BalanceOf<T>>();2390		final_fee.saturating_mul(coefficient).saturated_into::<TransactionPriority>()2391	}23922393    fn withdraw_fee(2394        &self,2395        who: &T::AccountId,2396        call: &T::Call,2397        info: &DispatchInfoOf<T::Call>,2398        len: usize,2399    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2400        let tip = self.0;24012402        // Set fee based on call type. Creating collection costs 1 Unique.2403        // All other transactions have traditional fees so far2404        // let fee = match call.is_sub_type() {2405        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2406        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2407        //                                                 // _ => <BalanceOf<T>>::from(100)2408        // };2409        let fee = Self::traditional_fee(len, info, tip);24102411        // Determine who is paying transaction fee based on ecnomic model2412        // Parse call to extract collection ID and access collection sponsor2413        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2414            Some(Call::create_item(collection_id, _owner, _properties)) => {24152416                // check free create limit2417                if <Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)2418                {2419                    <Collection<T>>::get(collection_id).sponsor2420                } else {2421                    T::AccountId::default()2422                }2423            }2424            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2425                2426                let _collection_limits = <Collection<T>>::get(collection_id).limits;2427                let _collection_mode = <Collection<T>>::get(collection_id).mode;24282429                // sponsor timeout2430                let sponsor_transfer = match _collection_mode {2431                    CollectionMode::NFT => {24322433                        // get correct limit2434                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2435                            _collection_limits.sponsor_transfer_timeout2436                        } else {2437                            ChainLimit::get().nft_sponsor_transfer_timeout2438                        };24392440                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2441                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2442                        let limit_time = basket + limit.into();2443                        if block_number >= limit_time {2444                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2445                            true2446                        }2447                        else {2448                            false2449                        }2450                    }2451                    CollectionMode::Fungible(_) => {24522453                        // get correct limit2454                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2455                            _collection_limits.sponsor_transfer_timeout2456                        } else {2457                            ChainLimit::get().fungible_sponsor_transfer_timeout2458                        };24592460                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2461                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2462                        if basket.iter().any(|i| i.address == _new_owner.clone())2463                        {2464                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2465                            let limit_time = item.start_block + limit.into();2466                            if block_number >= limit_time {2467                                basket.retain(|x| x.address == item.address);2468                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2469                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2470                                true2471                            }2472                            else {2473                                false2474                            }2475                        }2476                        else {2477                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2478                            true2479                        }2480                    }2481                    CollectionMode::ReFungible(_) => {24822483                        // get correct limit2484                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2485                            _collection_limits.sponsor_transfer_timeout2486                        } else {2487                            ChainLimit::get().refungible_sponsor_transfer_timeout2488                        };24892490                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2491                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2492                        let limit_time = basket + limit.into();2493                        if block_number >= limit_time {2494                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2495                            true2496                        } else {2497                            false2498                        }2499                    }2500                    _ => {2501                        false2502                    },2503                };25042505                if !sponsor_transfer {2506                    T::AccountId::default()2507                } else {2508                    <Collection<T>>::get(collection_id).sponsor2509                }2510            }25112512            _ => T::AccountId::default(),2513        };25142515        // Sponsor smart contracts2516        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {25172518            // On instantiation: set the contract owner2519            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {25202521                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2522                    code_hash,2523                    &data,2524                    &who,2525                );2526                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());25272528                T::AccountId::default()2529            },25302531            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2532            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {25332534                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());25352536                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2537                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2538                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2539                  2540                if !owned_contract && white_list_enabled {2541                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2542                        return Err(InvalidTransaction::Call.into());2543                    }2544                }25452546                let mut sponsor_transfer = false;2547                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2548                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2549                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2550                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2551                    let limit_time = last_tx_block + rate_limit;25522553                    if block_number >= limit_time {2554                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2555                        sponsor_transfer = true;2556                    }2557                } else {2558                    sponsor_transfer = false;2559                }2560               2561                2562                let mut sp = T::AccountId::default();2563                if sponsor_transfer {2564                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2565                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2566                            sp = called_contract;2567                        }2568                    }2569                }25702571                sp2572            },25732574            _ => sponsor,2575        };25762577        let mut who_pays_fee: T::AccountId = sponsor.clone();2578        if sponsor == T::AccountId::default() {2579            who_pays_fee = who.clone();2580        }25812582        // Only mess with balances if fee is not zero.2583        if fee.is_zero() {2584            return Ok((fee, None));2585        }25862587        match <T as transaction_payment::Trait>::Currency::withdraw(2588            &who_pays_fee,2589            fee,2590            if tip.is_zero() {2591                WithdrawReason::TransactionPayment.into()2592            } else {2593                WithdrawReason::TransactionPayment | WithdrawReason::Tip2594            },2595            ExistenceRequirement::KeepAlive,2596        ) {2597            Ok(imbalance) => Ok((fee, Some(imbalance))),2598            Err(_) => Err(InvalidTransaction::Payment.into()),2599        }2600    }2601}260226032604impl<T: Trait + Send + Sync> SignedExtension2605    for ChargeTransactionPayment<T>2606where2607    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2608    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2609{2610    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2611    type AccountId = T::AccountId;2612    type Call = T::Call;2613    type AdditionalSigned = ();2614    type Pre = (2615        BalanceOf<T>,2616        Self::AccountId,2617        Option<NegativeImbalanceOf<T>>,2618        BalanceOf<T>,2619    );2620    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2621        Ok(())2622    }26232624    fn validate(2625        &self,2626        who: &Self::AccountId,2627        call: &Self::Call,2628        info: &DispatchInfoOf<Self::Call>,2629        len: usize,2630    ) -> TransactionValidity {2631		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2632		Ok(ValidTransaction {2633			priority: Self::get_priority(len, info, fee),2634			..Default::default()2635		})2636    }26372638    fn pre_dispatch(2639        self,2640        who: &Self::AccountId,2641        call: &Self::Call,2642        info: &DispatchInfoOf<Self::Call>,2643        len: usize,2644    ) -> Result<Self::Pre, TransactionValidityError> {2645        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2646        Ok((self.0, who.clone(), imbalance, fee))2647    }26482649    fn post_dispatch(2650        pre: Self::Pre,2651        info: &DispatchInfoOf<Self::Call>,2652        post_info: &PostDispatchInfoOf<Self::Call>,2653        len: usize,2654        _result: &DispatchResult,2655    ) -> Result<(), TransactionValidityError> {2656        let (tip, who, imbalance, fee) = pre;2657        if let Some(payed) = imbalance {2658            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2659                len as u32, info, post_info, tip,2660            );2661            let refund = fee.saturating_sub(actual_fee);2662            let actual_payment =2663                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2664                    &who, refund,2665                ) {2666                    Ok(refund_imbalance) => {2667                        // The refund cannot be larger than the up front payed max weight.2668                        // `PostDispatchInfo::calc_unspent` guards against such a case.2669                        match payed.offset(refund_imbalance) {2670                            Ok(actual_payment) => actual_payment,2671                            Err(_) => return Err(InvalidTransaction::Payment.into()),2672                        }2673                    }2674                    // We do not recreate the account using the refund. The up front payment2675                    // is gone in that case.2676                    Err(_) => payed,2677                };2678            let imbalances = actual_payment.split(tip);2679            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2680                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2681            );2682        }2683        Ok(())2684    }2685}26862687// #endregion