git.delta.rocks / unique-network / refs/commits / 5743a92bf274

difftreelog

NFTPAR-280. Rate limit for CreateItem

str-mv2021-01-12parent: #1b2f5b5.patch.diff
in: master

1 file changed

modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
before · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, WithdrawReason,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial,29    },30    IsSubType, StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use pallet_contracts::ContractAddressFor;45use sp_runtime::traits::StaticLookup;4647#[cfg(test)]48mod mock;4950#[cfg(test)]51mod tests;5253mod default_weights;5455pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;56pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;57pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;5859// Structs60// #region6162pub type CollectionId = u32;63pub type TokenId = u32;64pub type DecimalPoints = u8;6566#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]67#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]68pub enum CollectionMode {69    Invalid,70    NFT,71    // decimal points72    Fungible(DecimalPoints),73    // decimal points74    ReFungible(DecimalPoints),75}7677impl Into<u8> for CollectionMode {78    fn into(self) -> u8 {79        match self {80            CollectionMode::Invalid => 0,81            CollectionMode::NFT => 1,82            CollectionMode::Fungible(_) => 2,83            CollectionMode::ReFungible(_) => 3,84        }85    }86}8788#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]89#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]90pub enum AccessMode {91    Normal,92    WhiteList,93}94impl Default for AccessMode {95    fn default() -> Self {96        Self::Normal97    }98}99100impl Default for CollectionMode {101    fn default() -> Self {102        Self::Invalid103    }104}105106#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]107#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]108pub enum SchemaVersion {109    ImageURL,110    Unique,111}112impl Default for SchemaVersion {113    fn default() -> Self {114        Self::ImageURL115    }116}117118#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]119#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]120pub struct Ownership<AccountId> {121    pub owner: AccountId,122    pub fraction: u128,123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct CollectionType<AccountId> {128    pub owner: AccountId,129    pub mode: CollectionMode,130    pub access: AccessMode,131    pub decimal_points: DecimalPoints,132    pub name: Vec<u16>,        // 64 include null escape char133    pub description: Vec<u16>, // 256 include null escape char134    pub token_prefix: Vec<u8>, // 16 include null escape char135    pub mint_mode: bool,136    pub offchain_schema: Vec<u8>,137    pub schema_version: SchemaVersion,138    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender139    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.140    pub limits: CollectionLimits, // Collection private restrictions 141    pub variable_on_chain_schema: Vec<u8>, //142    pub const_on_chain_schema: Vec<u8>, //143}144145#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]146#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]147pub struct NftItemType<AccountId> {148    pub owner: AccountId,149    pub const_data: Vec<u8>,150    pub variable_data: Vec<u8>,151}152153#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]154#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]155pub struct FungibleItemType {156    pub value: u128,157}158159#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]160#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]161pub struct ReFungibleItemType<AccountId> {162    pub owner: Vec<Ownership<AccountId>>,163    pub const_data: Vec<u8>,164    pub variable_data: Vec<u8>,165}166167// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]168// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169// pub struct VestingItem<AccountId, Moment> {170//     pub sender: AccountId,171//     pub recipient: AccountId,172//     pub collection_id: CollectionId,173//     pub item_id: TokenId,174//     pub amount: u64,175//     pub vesting_date: Moment,176// }177178#[derive(Encode, Decode, Debug, Clone, PartialEq)]179#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]180pub struct CollectionLimits {181    pub account_token_ownership_limit: u32,182    pub sponsored_data_size: u32,183    pub token_limit: u32,184185    // Timeouts for item types in passed blocks186    pub sponsor_transfer_timeout: u32,187}188189impl Default for CollectionLimits {190    fn default() -> CollectionLimits {191        CollectionLimits { 192            account_token_ownership_limit: 10_000_000, 193            token_limit: u32::max_value(),194            sponsored_data_size: u32::max_value(), 195            sponsor_transfer_timeout: 14400 }196    }197}198199#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]200#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]201pub struct ChainLimits {202    pub collection_numbers_limit: u32,203    pub account_token_ownership_limit: u32,204    pub collections_admins_limit: u64,205    pub custom_data_limit: u32,206207    // Timeouts for item types in passed blocks208    pub nft_sponsor_transfer_timeout: u32,209    pub fungible_sponsor_transfer_timeout: u32,210    pub refungible_sponsor_transfer_timeout: u32,211}212213pub trait WeightInfo {214	fn create_collection() -> Weight;215	fn destroy_collection() -> Weight;216	fn add_to_white_list() -> Weight;217	fn remove_from_white_list() -> Weight;218    fn set_public_access_mode() -> Weight;219    fn set_mint_permission() -> Weight;220    fn change_collection_owner() -> Weight;221    fn add_collection_admin() -> Weight;222    fn remove_collection_admin() -> Weight;223    fn set_collection_sponsor() -> Weight;224    fn confirm_sponsorship() -> Weight;225    fn remove_collection_sponsor() -> Weight;226    fn create_item(s: usize) -> Weight;227    fn burn_item() -> Weight;228    fn transfer() -> Weight;229    fn approve() -> Weight;230    fn transfer_from() -> Weight;231    fn set_offchain_schema() -> Weight;232    fn set_const_on_chain_schema() -> Weight;233    fn set_variable_on_chain_schema() -> Weight;234    fn set_variable_meta_data() -> Weight;235    fn enable_contract_sponsoring() -> Weight;236    fn set_schema_version() -> Weight;237    fn set_chain_limits() -> Weight;238    fn set_contract_sponsoring_rate_limit() -> Weight;239    fn toggle_contract_white_list() -> Weight;240    fn add_to_contract_white_list() -> Weight;241    fn remove_from_contract_white_list() -> Weight;242    fn set_collection_limits() -> Weight;243}244245#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]246#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]247pub struct CreateNftData {248    pub const_data: Vec<u8>,249    pub variable_data: Vec<u8>,250}251252#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]253#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]254pub struct CreateFungibleData {255    pub value: u128,256}257258#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]259#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]260pub struct CreateReFungibleData {261    pub const_data: Vec<u8>,262    pub variable_data: Vec<u8>,263}264265#[derive(Encode, Decode, Debug, Clone, PartialEq)]266#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]267pub enum CreateItemData {268    NFT(CreateNftData),269    Fungible(CreateFungibleData),270    ReFungible(CreateReFungibleData),271}272273impl CreateItemData {274    pub fn len(&self) -> usize {275        let len = match self {276            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),277            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),278            _ => 0279        };280        281        return len;282    }283}284285impl From<CreateNftData> for CreateItemData {286    fn from(item: CreateNftData) -> Self {287        CreateItemData::NFT(item)288    }289}290291impl From<CreateReFungibleData> for CreateItemData {292    fn from(item: CreateReFungibleData) -> Self {293        CreateItemData::ReFungible(item)294    }295}296297impl From<CreateFungibleData> for CreateItemData {298    fn from(item: CreateFungibleData) -> Self {299        CreateItemData::Fungible(item)300    }301}302303304decl_error! {305	/// Error for non-fungible-token module.306	pub enum Error for Module<T: Trait> {307        /// Total collections bound exceeded.308        TotalCollectionsLimitExceeded,309		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.310        CollectionDecimalPointLimitExceeded, 311        /// Collection name can not be longer than 63 char.312        CollectionNameLimitExceeded, 313        /// Collection description can not be longer than 255 char.314        CollectionDescriptionLimitExceeded, 315        /// Token prefix can not be longer than 15 char.316        CollectionTokenPrefixLimitExceeded,317        /// This collection does not exist.318        CollectionNotFound,319        /// Item not exists.320        TokenNotFound,321        /// Arithmetic calculation overflow.322        NumOverflow,       323        /// Account already has admin role.324        AlreadyAdmin,  325        /// You do not own this collection.326        NoPermission,327        /// This address is not set as sponsor, use setCollectionSponsor first.328        ConfirmUnsetSponsorFail,329        /// Collection is not in mint mode.330        PublicMintingNotAllowed,331        /// Sender parameter and item owner must be equal.332        MustBeTokenOwner,333        /// Item balance not enough.334        TokenValueTooLow,335        /// Size of item is too large.336        NftSizeLimitExceeded,337        /// No approve found338        ApproveNotFound,339        /// Requested value more than approved.340        TokenValueNotEnough,341        /// Only approved addresses can call this method.342        ApproveRequired,343        /// Address is not in white list.344        AddresNotInWhiteList,345        /// Number of collection admins bound exceeded.346        CollectionAdminsLimitExceeded,347        /// Owned tokens by a single address bound exceeded.348        AddressOwnershipLimitExceeded,349        /// Length of items properties must be greater than 0.350        EmptyArgument,351        /// const_data exceeded data limit.352        TokenConstDataLimitExceeded,353        /// variable_data exceeded data limit.354        TokenVariableDataLimitExceeded,355        /// Not NFT item data used to mint in NFT collection.356        NotNftDataUsedToMintNftCollectionToken,357        /// Not Fungible item data used to mint in Fungible collection.358        NotFungibleDataUsedToMintFungibleCollectionToken,359        /// Not Re Fungible item data used to mint in Re Fungible collection.360        NotReFungibleDataUsedToMintReFungibleCollectionToken,361        /// Unexpected collection type.362        UnexpectedCollectionType,363        /// Can't store metadata in fungible tokens.364        CantStoreMetadataInFungibleTokens,365        /// Collection token limit exceeded366        CollectionTokenLimitExceeded,367        /// Account token limit exceeded per collection368        AccountTokenLimitExceeded,369        /// Collection limit bounds per collection exceeded370        CollectionLimitBoundsExceeded371	}372}373374pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {375    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;376377    /// Weight information for extrinsics in this pallet.378	type WeightInfo: WeightInfo;379}380381#[cfg(feature = "runtime-benchmarks")]382mod benchmarking;383384// #endregion385386decl_storage! {387    trait Store for Module<T: Trait> as Nft {388389        // Private members390        NextCollectionID: CollectionId;391        CreatedCollectionCount: u32;392        ChainVersion: u64;393        ItemListIndex: map hasher(identity) CollectionId => TokenId;394395        // Chain limits struct396        pub ChainLimit get(fn chain_limit) config(): ChainLimits;397398        // Bound counters399        CollectionCount: u32;400        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;401402        // Basic collections403        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;404        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;405        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;406407        /// Balance owner per collection map408        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;409410        /// second parameter: item id + owner account id + spender account id411        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;412413        /// Item collections414        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;415        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;416        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;417418        /// Index list419        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;420421        /// Tokens transfer baskets422        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;423        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;424        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;425426        // Contract Sponsorship and Ownership427        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;428        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;429        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;430        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;431        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 432        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 433    }434    add_extra_genesis {435        build(|config: &GenesisConfig<T>| {436            // Modification of storage437            for (_num, _c) in &config.collection {438                <Module<T>>::init_collection(_c);439            }440441            for (_num, _c, _i) in &config.nft_item_id {442                <Module<T>>::init_nft_token(*_c, _i);443            }444445            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {446                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);447            }448449            for (_num, _c, _i) in &config.refungible_item_id {450                <Module<T>>::init_refungible_token(*_c, _i);451            }452        })453    }454}455456decl_event!(457    pub enum Event<T>458    where459        AccountId = <T as system::Trait>::AccountId,460    {461        /// New collection was created462        /// 463        /// # Arguments464        /// 465        /// * collection_id: Globally unique identifier of newly created collection.466        /// 467        /// * mode: [CollectionMode] converted into u8.468        /// 469        /// * account_id: Collection owner.470        Created(CollectionId, u8, AccountId),471472        /// New item was created.473        /// 474        /// # Arguments475        /// 476        /// * collection_id: Id of the collection where item was created.477        /// 478        /// * item_id: Id of an item. Unique within the collection.479        ItemCreated(CollectionId, TokenId),480481        /// Collection item was burned.482        /// 483        /// # Arguments484        /// 485        /// collection_id.486        /// 487        /// item_id: Identifier of burned NFT.488        ItemDestroyed(CollectionId, TokenId),489    }490);491492decl_module! {493    pub struct Module<T: Trait> for enum Call where origin: T::Origin {494495        fn deposit_event() = default;496        type Error = Error<T>;497498        fn on_initialize(now: T::BlockNumber) -> Weight {499500            if ChainVersion::get() < 2501            {502                let value = NextCollectionID::get();503                CreatedCollectionCount::put(value);504                ChainVersion::put(2);505            }506507            0508        }509510        /// 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.511        /// 512        /// # Permissions513        /// 514        /// * Anyone.515        /// 516        /// # Arguments517        /// 518        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.519        /// 520        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.521        /// 522        /// * token_prefix: UTF-8 string with token prefix.523        /// 524        /// * mode: [CollectionMode] collection type and type dependent data.525        // returns collection ID526        #[weight = T::WeightInfo::create_collection()]527        pub fn create_collection(origin,528                                 collection_name: Vec<u16>,529                                 collection_description: Vec<u16>,530                                 token_prefix: Vec<u8>,531                                 mode: CollectionMode) -> DispatchResult {532533            // Anyone can create a collection534            let who = ensure_signed(origin)?;535536            let decimal_points = match mode {537                CollectionMode::Fungible(points) => points,538                CollectionMode::ReFungible(points) => points,539                _ => 0540            };541542            // bound Total number of collections543            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);544545            // check params546            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);547            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);548            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);549            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);550551            // Generate next collection ID552            let next_id = CreatedCollectionCount::get()553                .checked_add(1)554                .ok_or(Error::<T>::NumOverflow)?;555556            // bound counter557            let total = CollectionCount::get()558                .checked_add(1)559                .ok_or(Error::<T>::NumOverflow)?;560561            CreatedCollectionCount::put(next_id);562            CollectionCount::put(total);563564            // Create new collection565            let new_collection = CollectionType {566                owner: who.clone(),567                name: collection_name,568                mode: mode.clone(),569                mint_mode: false,570                access: AccessMode::Normal,571                description: collection_description,572                decimal_points: decimal_points,573                token_prefix: token_prefix,574                offchain_schema: Vec::new(),575                schema_version: SchemaVersion::ImageURL,576                sponsor: T::AccountId::default(),577                sponsor_confirmed: false,578                variable_on_chain_schema: Vec::new(),579                const_on_chain_schema: Vec::new(),580                limits: CollectionLimits::default(),581            };582583            // Add new collection to map584            <Collection<T>>::insert(next_id, new_collection);585586            // call event587            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));588589            Ok(())590        }591592        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.593        /// 594        /// # Permissions595        /// 596        /// * Collection Owner.597        /// 598        /// # Arguments599        /// 600        /// * collection_id: collection to destroy.601        #[weight = T::WeightInfo::destroy_collection()]602        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {603604            let sender = ensure_signed(origin)?;605            Self::check_owner_permissions(collection_id, sender)?;606607            <AddressTokens<T>>::remove_prefix(collection_id);608            <Allowances<T>>::remove_prefix(collection_id);609            <Balance<T>>::remove_prefix(collection_id);610            <ItemListIndex>::remove(collection_id);611            <AdminList<T>>::remove(collection_id);612            <Collection<T>>::remove(collection_id);613            <WhiteList<T>>::remove_prefix(collection_id);614615            <NftItemList<T>>::remove_prefix(collection_id);616            <FungibleItemList<T>>::remove_prefix(collection_id);617            <ReFungibleItemList<T>>::remove_prefix(collection_id);618619            <NftTransferBasket<T>>::remove_prefix(collection_id);620            <FungibleTransferBasket<T>>::remove_prefix(collection_id);621            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);622623            if CollectionCount::get() > 0624            {625                // bound couter626                let total = CollectionCount::get()627                    .checked_sub(1)628                    .ok_or(Error::<T>::NumOverflow)?;629630                CollectionCount::put(total);631            }632633            Ok(())634        }635636        /// Add an address to white list.637        /// 638        /// # Permissions639        /// 640        /// * Collection Owner641        /// * Collection Admin642        /// 643        /// # Arguments644        /// 645        /// * collection_id.646        /// 647        /// * address.648        #[weight = T::WeightInfo::add_to_white_list()]649        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{650651            let sender = ensure_signed(origin)?;652            Self::check_owner_or_admin_permissions(collection_id, sender)?;653654            <WhiteList<T>>::insert(collection_id, address, true);655            656            Ok(())657        }658659        /// Remove an address from white list.660        /// 661        /// # Permissions662        /// 663        /// * Collection Owner664        /// * Collection Admin665        /// 666        /// # Arguments667        /// 668        /// * collection_id.669        /// 670        /// * address.671        #[weight = T::WeightInfo::remove_from_white_list()]672        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{673674            let sender = ensure_signed(origin)?;675            Self::check_owner_or_admin_permissions(collection_id, sender)?;676677            <WhiteList<T>>::remove(collection_id, address);678679            Ok(())680        }681682        /// Toggle between normal and white list access for the methods with access for `Anyone`.683        /// 684        /// # Permissions685        /// 686        /// * Collection Owner.687        /// 688        /// # Arguments689        /// 690        /// * collection_id.691        /// 692        /// * mode: [AccessMode]693        #[weight = T::WeightInfo::set_public_access_mode()]694        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult695        {696            let sender = ensure_signed(origin)?;697698            Self::check_owner_permissions(collection_id, sender)?;699            let mut target_collection = <Collection<T>>::get(collection_id);700            target_collection.access = mode;701            <Collection<T>>::insert(collection_id, target_collection);702703            Ok(())704        }705706        /// Allows Anyone to create tokens if:707        /// * White List is enabled, and708        /// * Address is added to white list, and709        /// * This method was called with True parameter710        /// 711        /// # Permissions712        /// * Collection Owner713        ///714        /// # Arguments715        /// 716        /// * collection_id.717        /// 718        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.719        #[weight = T::WeightInfo::set_mint_permission()]720        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> 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.mint_mode = mint_permission;727            <Collection<T>>::insert(collection_id, target_collection);728729            Ok(())730        }731732        /// Change the owner of the collection.733        /// 734        /// # Permissions735        /// 736        /// * Collection Owner.737        /// 738        /// # Arguments739        /// 740        /// * collection_id.741        /// 742        /// * new_owner.743        #[weight = T::WeightInfo::change_collection_owner()]744        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {745746            let sender = ensure_signed(origin)?;747            Self::check_owner_permissions(collection_id, sender)?;748            let mut target_collection = <Collection<T>>::get(collection_id);749            target_collection.owner = new_owner;750            <Collection<T>>::insert(collection_id, target_collection);751752            Ok(())753        }754755        /// Adds an admin of the Collection.756        /// 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. 757        /// 758        /// # Permissions759        /// 760        /// * Collection Owner.761        /// * Collection Admin.762        /// 763        /// # Arguments764        /// 765        /// * collection_id: ID of the Collection to add admin for.766        /// 767        /// * new_admin_id: Address of new admin to add.768        #[weight = T::WeightInfo::add_collection_admin()]769        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {770771            let sender = ensure_signed(origin)?;772            Self::check_owner_or_admin_permissions(collection_id, sender)?;773            let mut admin_arr: Vec<T::AccountId> = Vec::new();774775            if <AdminList<T>>::contains_key(collection_id)776            {777                admin_arr = <AdminList<T>>::get(collection_id);778                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);779            }780781            // Number of collection admins782            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);783784            admin_arr.push(new_admin_id);785            <AdminList<T>>::insert(collection_id, admin_arr);786787            Ok(())788        }789790        /// 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.791        ///792        /// # Permissions793        /// 794        /// * Collection Owner.795        /// * Collection Admin.796        /// 797        /// # Arguments798        /// 799        /// * collection_id: ID of the Collection to remove admin for.800        /// 801        /// * account_id: Address of admin to remove.802        #[weight = T::WeightInfo::remove_collection_admin()]803        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {804805            let sender = ensure_signed(origin)?;806            Self::check_owner_or_admin_permissions(collection_id, sender)?;807808            if <AdminList<T>>::contains_key(collection_id)809            {810                let mut admin_arr = <AdminList<T>>::get(collection_id);811                admin_arr.retain(|i| *i != account_id);812                <AdminList<T>>::insert(collection_id, admin_arr);813            }814815            Ok(())816        }817818        /// # Permissions819        /// 820        /// * Collection Owner821        /// 822        /// # Arguments823        /// 824        /// * collection_id.825        /// 826        /// * new_sponsor.827        #[weight = T::WeightInfo::set_collection_sponsor()]828        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {829830            let sender = ensure_signed(origin)?;831            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);832833            let mut target_collection = <Collection<T>>::get(collection_id);834            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);835836            target_collection.sponsor = new_sponsor;837            target_collection.sponsor_confirmed = false;838            <Collection<T>>::insert(collection_id, target_collection);839840            Ok(())841        }842843        /// # Permissions844        /// 845        /// * Sponsor.846        /// 847        /// # Arguments848        /// 849        /// * collection_id.850        #[weight = T::WeightInfo::confirm_sponsorship()]851        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {852853            let sender = ensure_signed(origin)?;854            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);855856            let mut target_collection = <Collection<T>>::get(collection_id);857            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);858859            target_collection.sponsor_confirmed = true;860            <Collection<T>>::insert(collection_id, target_collection);861862            Ok(())863        }864865        /// Switch back to pay-per-own-transaction model.866        ///867        /// # Permissions868        ///869        /// * Collection owner.870        /// 871        /// # Arguments872        /// 873        /// * collection_id.874        #[weight = T::WeightInfo::remove_collection_sponsor()]875        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {876877            let sender = ensure_signed(origin)?;878            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);879880            let mut target_collection = <Collection<T>>::get(collection_id);881            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);882883            target_collection.sponsor = T::AccountId::default();884            target_collection.sponsor_confirmed = false;885            <Collection<T>>::insert(collection_id, target_collection);886887            Ok(())888        }889890        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.891        /// 892        /// # Permissions893        /// 894        /// * Collection Owner.895        /// * Collection Admin.896        /// * Anyone if897        ///     * White List is enabled, and898        ///     * Address is added to white list, and899        ///     * MintPermission is enabled (see SetMintPermission method)900        /// 901        /// # Arguments902        /// 903        /// * collection_id: ID of the collection.904        /// 905        /// * owner: Address, initial owner of the NFT.906        ///907        /// * data: Token data to store on chain.908        // #[weight =909        // (130_000_000 as Weight)910        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))911        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))912        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]913914        #[weight = T::WeightInfo::create_item(data.len())]915        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {916917            let sender = ensure_signed(origin)?;918919            Self::collection_exists(collection_id)?;920921            let target_collection = <Collection<T>>::get(collection_id);922923            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;924            Self::validate_create_item_args(&target_collection, &data)?;925            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;926927            Ok(())928        }929930        /// This method creates multiple instances of NFT Collection created with CreateCollection method.931        /// 932        /// # Permissions933        /// 934        /// * Collection Owner.935        /// * Collection Admin.936        /// * Anyone if937        ///     * White List is enabled, and938        ///     * Address is added to white list, and939        ///     * MintPermission is enabled (see SetMintPermission method)940        /// 941        /// # Arguments942        /// 943        /// * collection_id: ID of the collection.944        /// 945        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].946        /// 947        /// * owner: Address, initial owner of the NFT.948        #[weight = T::WeightInfo::create_item(items_data.into_iter()949                               .map(|data| { data.len() })950                               .sum())]951        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {952953            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);954            let sender = ensure_signed(origin)?;955956            Self::collection_exists(collection_id)?;957            let target_collection = <Collection<T>>::get(collection_id);958959            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;960961            for data in &items_data {962                Self::validate_create_item_args(&target_collection, data)?;963            }964            for data in &items_data {965                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;966            }967968            Ok(())969        }970971        /// Destroys a concrete instance of NFT.972        /// 973        /// # Permissions974        /// 975        /// * Collection Owner.976        /// * Collection Admin.977        /// * Current NFT Owner.978        /// 979        /// # Arguments980        /// 981        /// * collection_id: ID of the collection.982        /// 983        /// * item_id: ID of NFT to burn.984        #[weight = T::WeightInfo::burn_item()]985        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {986987            let sender = ensure_signed(origin)?;988            Self::collection_exists(collection_id)?;989990            // Transfer permissions check991            let target_collection = <Collection<T>>::get(collection_id);992            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||993                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),994                Error::<T>::NoPermission);995996            if target_collection.access == AccessMode::WhiteList {997                Self::check_white_list(collection_id, &sender)?;998            }9991000            match target_collection.mode1001            {1002                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1003                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1004                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1005                _ => ()1006            };10071008            // call event1009            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10101011            Ok(())1012        }10131014        /// Change ownership of the token.1015        /// 1016        /// # Permissions1017        /// 1018        /// * Collection Owner1019        /// * Collection Admin1020        /// * Current NFT owner1021        ///1022        /// # Arguments1023        /// 1024        /// * recipient: Address of token recipient.1025        /// 1026        /// * collection_id.1027        /// 1028        /// * item_id: ID of the item1029        ///     * Non-Fungible Mode: Required.1030        ///     * Fungible Mode: Ignored.1031        ///     * Re-Fungible Mode: Required.1032        /// 1033        /// * value: Amount to transfer.1034        ///     * Non-Fungible Mode: Ignored1035        ///     * Fungible Mode: Must specify transferred amount1036        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1037        #[weight = T::WeightInfo::transfer()]1038        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10391040            let sender = ensure_signed(origin)?;1041            let target_collection = <Collection<T>>::get(collection_id);10421043            // Limits check1044            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;10451046            // Transfer permissions check1047            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1048                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1049                Error::<T>::NoPermission);10501051            if target_collection.access == AccessMode::WhiteList {1052                Self::check_white_list(collection_id, &sender)?;1053                Self::check_white_list(collection_id, &recipient)?;1054            }10551056            match target_collection.mode1057            {1058                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1059                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1060                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1061                _ => ()1062            };10631064            Ok(())1065        }10661067        /// Set, change, or remove approved address to transfer the ownership of the NFT.1068        /// 1069        /// # Permissions1070        /// 1071        /// * Collection Owner1072        /// * Collection Admin1073        /// * Current NFT owner1074        /// 1075        /// # Arguments1076        /// 1077        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1078        /// 1079        /// * collection_id.1080        /// 1081        /// * item_id: ID of the item.1082        #[weight = T::WeightInfo::approve()]1083        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {10841085            let sender = ensure_signed(origin)?;10861087            // Transfer permissions check1088            let target_collection = <Collection<T>>::get(collection_id);1089            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1090                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1091                Error::<T>::NoPermission);10921093            if target_collection.access == AccessMode::WhiteList {1094                Self::check_white_list(collection_id, &sender)?;1095                Self::check_white_list(collection_id, &spender)?;1096            }10971098            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1099            let mut allowance: u128 = amount;1100            if allowance_exists {1101                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1102            }1103            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11041105            Ok(())1106        }1107        1108        /// 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.1109        /// 1110        /// # Permissions1111        /// * Collection Owner1112        /// * Collection Admin1113        /// * Current NFT owner1114        /// * Address approved by current NFT owner1115        /// 1116        /// # Arguments1117        /// 1118        /// * from: Address that owns token.1119        /// 1120        /// * recipient: Address of token recipient.1121        /// 1122        /// * collection_id.1123        /// 1124        /// * item_id: ID of the item.1125        /// 1126        /// * value: Amount to transfer.1127        #[weight = T::WeightInfo::transfer_from()]1128        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11291130            let sender = ensure_signed(origin)?;1131            let mut appoved_transfer = false;11321133            // Check approval1134            let mut approval: u128 = 0;1135            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &recipient)) {1136                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &recipient));1137                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1138                appoved_transfer = true;1139            }11401141            let target_collection = <Collection<T>>::get(collection_id);11421143            // Limits check1144            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11451146            // Transfer permissions check         1147            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1148                Error::<T>::NoPermission);11491150            if target_collection.access == AccessMode::WhiteList {1151                Self::check_white_list(collection_id, &sender)?;1152                Self::check_white_list(collection_id, &recipient)?;1153            }11541155            // Reduce approval by transferred amount or remove if remaining approval drops to 01156            if approval - value > 0 {1157                <Allowances<T>>::insert(collection_id, (item_id, &from, &recipient), approval - value);1158            }1159            else {1160                <Allowances<T>>::remove(collection_id, (item_id, &from, &recipient));1161            }11621163            match target_collection.mode1164            {1165                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1166                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1167                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1168                _ => ()1169            };11701171            Ok(())1172        }11731174        #[weight = 0]1175        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11761177            // let no_perm_mes = "You do not have permissions to modify this collection";1178            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1179            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1180            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11811182            // // on_nft_received  call11831184            // Self::transfer(origin, collection_id, item_id, new_owner)?;11851186            Ok(())1187        }11881189        /// Set off-chain data schema.1190        /// 1191        /// # Permissions1192        /// 1193        /// * Collection Owner1194        /// * Collection Admin1195        /// 1196        /// # Arguments1197        /// 1198        /// * collection_id.1199        /// 1200        /// * schema: String representing the offchain data schema.1201        #[weight = T::WeightInfo::set_variable_meta_data()]1202        pub fn set_variable_meta_data (1203            origin,1204            collection_id: CollectionId,1205            item_id: TokenId,1206            data: Vec<u8>1207        ) -> DispatchResult {1208            let sender = ensure_signed(origin)?;1209            1210            Self::collection_exists(collection_id)?;1211            1212            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12131214            // Modify permissions check1215            let target_collection = <Collection<T>>::get(collection_id);1216            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1217                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1218                Error::<T>::NoPermission);12191220            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12211222            match target_collection.mode1223            {1224                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1225                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1226                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1227                _ => fail!(Error::<T>::UnexpectedCollectionType)1228            };12291230            Ok(())1231        }1232 1233        /// Set schema standard1234        /// ImageURL1235        /// Unique1236        /// 1237        /// # Permissions1238        /// 1239        /// * Collection Owner1240        /// * Collection Admin1241        /// 1242        /// # Arguments1243        /// 1244        /// * collection_id.1245        /// 1246        /// * schema: SchemaVersion: enum1247        #[weight = T::WeightInfo::set_schema_version()]1248        pub fn set_schema_version(1249            origin,1250            collection_id: CollectionId,1251            version: SchemaVersion1252        ) -> DispatchResult {1253            let sender = ensure_signed(origin)?;1254            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1255            let mut target_collection = <Collection<T>>::get(collection_id);1256            target_collection.schema_version = version;1257            <Collection<T>>::insert(collection_id, target_collection);12581259            Ok(())1260        }12611262        /// Set off-chain data schema.1263        /// 1264        /// # Permissions1265        /// 1266        /// * Collection Owner1267        /// * Collection Admin1268        /// 1269        /// # Arguments1270        /// 1271        /// * collection_id.1272        /// 1273        /// * schema: String representing the offchain data schema.1274        #[weight = T::WeightInfo::set_offchain_schema()]1275        pub fn set_offchain_schema(1276            origin,1277            collection_id: CollectionId,1278            schema: Vec<u8>1279        ) -> DispatchResult {1280            let sender = ensure_signed(origin)?;1281            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12821283            let mut target_collection = <Collection<T>>::get(collection_id);1284            target_collection.offchain_schema = schema;1285            <Collection<T>>::insert(collection_id, target_collection);12861287            Ok(())1288        }12891290        /// Set const on-chain data schema.1291        /// 1292        /// # Permissions1293        /// 1294        /// * Collection Owner1295        /// * Collection Admin1296        /// 1297        /// # Arguments1298        /// 1299        /// * collection_id.1300        /// 1301        /// * schema: String representing the const on-chain data schema.1302        #[weight = T::WeightInfo::set_const_on_chain_schema()]1303        pub fn set_const_on_chain_schema (1304            origin,1305            collection_id: CollectionId,1306            schema: Vec<u8>1307        ) -> DispatchResult {1308            let sender = ensure_signed(origin)?;1309            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13101311            let mut target_collection = <Collection<T>>::get(collection_id);1312            target_collection.const_on_chain_schema = schema;1313            <Collection<T>>::insert(collection_id, target_collection);13141315            Ok(())1316        }13171318        /// Set variable on-chain data schema.1319        /// 1320        /// # Permissions1321        /// 1322        /// * Collection Owner1323        /// * Collection Admin1324        /// 1325        /// # Arguments1326        /// 1327        /// * collection_id.1328        /// 1329        /// * schema: String representing the variable on-chain data schema.1330        #[weight = T::WeightInfo::set_const_on_chain_schema()]1331        pub fn set_variable_on_chain_schema (1332            origin,1333            collection_id: CollectionId,1334            schema: Vec<u8>1335        ) -> DispatchResult {1336            let sender = ensure_signed(origin)?;1337            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13381339            let mut target_collection = <Collection<T>>::get(collection_id);1340            target_collection.variable_on_chain_schema = schema;1341            <Collection<T>>::insert(collection_id, target_collection);13421343            Ok(())1344        }13451346        // Sudo permissions function1347        #[weight = T::WeightInfo::set_chain_limits()]1348        pub fn set_chain_limits(1349            origin,1350            limits: ChainLimits1351        ) -> DispatchResult {13521353            #[cfg(not(feature = "runtime-benchmarks"))]1354            ensure_root(origin)?;13551356            <ChainLimit>::put(limits);1357            Ok(())1358        }13591360        /// Enable smart contract self-sponsoring.1361        /// 1362        /// # Permissions1363        /// 1364        /// * Contract Owner1365        /// 1366        /// # Arguments1367        /// 1368        /// * contract address1369        /// * enable flag1370        /// 1371        #[weight = T::WeightInfo::enable_contract_sponsoring()]1372        pub fn enable_contract_sponsoring(1373            origin,1374            contract_address: T::AccountId,1375            enable: bool1376        ) -> DispatchResult {13771378            let sender = ensure_signed(origin)?;13791380            #[cfg(feature = "runtime-benchmarks")]1381            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13821383            Self::ensure_contract_owned(sender, &contract_address)?;13841385            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1386            Ok(())1387        }13881389        /// Set the rate limit for contract sponsoring to specified number of blocks.1390        /// 1391        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1392        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1393        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1394        /// from contract endowment if there are at least B blocks between such transactions. 1395        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1396        /// 1397        /// # Permissions1398        /// 1399        /// * Contract Owner1400        /// 1401        /// # Arguments1402        /// 1403        /// -`contract_address`: Address of the contract to sponsor1404        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1405        /// 1406        #[weight = T::WeightInfo::set_contract_sponsoring_rate_limit()]1407        pub fn set_contract_sponsoring_rate_limit(1408            origin,1409            contract_address: T::AccountId,1410            rate_limit: T::BlockNumber1411        ) -> DispatchResult {1412            let sender = ensure_signed(origin)?;14131414            #[cfg(feature = "runtime-benchmarks")]1415            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14161417            Self::ensure_contract_owned(sender, &contract_address)?;1418            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1419            Ok(())1420        }14211422        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1423        /// 1424        /// # Permissions1425        /// 1426        /// * Address that deployed smart contract.1427        /// 1428        /// # Arguments1429        /// 1430        /// -`contract_address`: Address of the contract.1431        /// 1432        /// - `enable`: .  1433        #[weight = T::WeightInfo::toggle_contract_white_list()]1434        pub fn toggle_contract_white_list(1435            origin,1436            contract_address: T::AccountId,1437            enable: bool1438        ) -> DispatchResult {1439            let sender = ensure_signed(origin)?;14401441            #[cfg(feature = "runtime-benchmarks")]1442            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14431444            Self::ensure_contract_owned(sender, &contract_address)?;1445            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1446            Ok(())1447        }1448        1449        /// Add an address to smart contract white list.1450        /// 1451        /// # Permissions1452        /// 1453        /// * Address that deployed smart contract.1454        /// 1455        /// # Arguments1456        /// 1457        /// -`contract_address`: Address of the contract.1458        ///1459        /// -`account_address`: Address to add.1460        #[weight = T::WeightInfo::add_to_contract_white_list()]1461        pub fn add_to_contract_white_list(1462            origin,1463            contract_address: T::AccountId,1464            account_address: T::AccountId1465        ) -> DispatchResult {1466            let sender = ensure_signed(origin)?;14671468            #[cfg(feature = "runtime-benchmarks")]1469            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1470            1471            Self::ensure_contract_owned(sender, &contract_address)?;      1472            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1473            Ok(())1474        }14751476        #[weight = T::WeightInfo::add_to_contract_white_list()]1477        pub fn wasm_dummy_method(1478            origin1479        ) -> DispatchResult {1480            let sender = ensure_signed(origin)?;1481            Ok(())1482        }14831484        /// Remove an address from smart contract white list.1485        /// 1486        /// # Permissions1487        /// 1488        /// * Address that deployed smart contract.1489        /// 1490        /// # Arguments1491        /// 1492        /// -`contract_address`: Address of the contract.1493        ///1494        /// -`account_address`: Address to remove.1495        #[weight = T::WeightInfo::remove_from_contract_white_list()]1496        pub fn remove_from_contract_white_list(1497            origin,1498            contract_address: T::AccountId,1499            account_address: T::AccountId1500        ) -> DispatchResult {1501            let sender = ensure_signed(origin)?;15021503            #[cfg(feature = "runtime-benchmarks")]1504            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15051506            Self::ensure_contract_owned(sender, &contract_address)?;1507            <ContractWhiteList<T>>::remove(contract_address, account_address);1508            Ok(())1509        }15101511        #[weight = T::WeightInfo::set_collection_limits()]1512        pub fn set_collection_limits(1513            origin,1514            collection_id: u32,1515            limits: CollectionLimits,1516        ) -> DispatchResult {1517            let sender = ensure_signed(origin)?;1518            Self::check_owner_permissions(collection_id, sender.clone())?;1519            let mut target_collection = <Collection<T>>::get(collection_id);1520            let chain_limits = ChainLimit::get();1521            let climits = target_collection.limits;15221523            // collection bounds1524            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1525                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1526                Error::<T>::CollectionLimitBoundsExceeded);15271528            // token_limit   check  prev1529            ensure!(climits.token_limit > limits.token_limit && 1530                limits.token_limit <= chain_limits.account_token_ownership_limit, 1531                Error::<T>::AccountTokenLimitExceeded);15321533            target_collection.limits = limits;1534            <Collection<T>>::insert(collection_id, target_collection);15351536            Ok(())1537        } 1538    }1539}15401541impl<T: Trait> Module<T> {15421543    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15441545        // check token limit and account token limit1546        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1547        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1548        1549        Ok(())1550    }15511552    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15531554        // check token limit and account token limit1555        let total_items: u32 = ItemListIndex::get(collection_id);1556        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1557        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1558        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15591560        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1561            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1562            Self::check_white_list(collection_id, owner)?;1563            Self::check_white_list(collection_id, sender)?;1564        }15651566        Ok(())1567    }15681569    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1570        match target_collection.mode1571        {1572            CollectionMode::NFT => {1573                if let CreateItemData::NFT(data) = data {1574                    // check sizes1575                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1576                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1577                } else {1578                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1579                }1580            },1581            CollectionMode::Fungible(_) => {1582                if let CreateItemData::Fungible(_) = data {1583                } else {1584                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1585                }1586            },1587            CollectionMode::ReFungible(_) => {1588                if let CreateItemData::ReFungible(data) = data {15891590                    // check sizes1591                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1592                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1593                } else {1594                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1595                }1596            },1597            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1598        };15991600        Ok(())1601    }16021603    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1604        match data1605        {1606            CreateItemData::NFT(data) => {1607                let item = NftItemType {1608                    owner,1609                    const_data: data.const_data,1610                    variable_data: data.variable_data1611                };16121613                Self::add_nft_item(collection_id, item)?;1614            },1615            CreateItemData::Fungible(data) => {1616                Self::add_fungible_item(collection_id, &owner, data.value)?;1617            },1618            CreateItemData::ReFungible(data) => {1619                let mut owner_list = Vec::new();1620                let value = (10 as u128).pow(collection.decimal_points as u32);1621                owner_list.push(Ownership {owner: owner.clone(), fraction: value});16221623                let item = ReFungibleItemType {1624                    owner: owner_list,1625                    const_data: data.const_data,1626                    variable_data: data.variable_data1627                };16281629                Self::add_refungible_item(collection_id, item)?;1630            }1631        };16321633        // call event1634        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16351636        Ok(())1637    }16381639    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {16401641        // Does new owner already have an account?1642        let mut balance: u128 = 0;1643        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1644            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1645        } 16461647        // Mint 1648        let item = FungibleItemType {1649            value: balance + value1650        };1651        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16521653        // Update balance1654        let new_balance = <Balance<T>>::get(collection_id, owner)1655            .checked_add(value)1656            .ok_or(Error::<T>::NumOverflow)?;1657        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);16581659        Ok(())1660    }16611662    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1663        let current_index = <ItemListIndex>::get(collection_id)1664            .checked_add(1)1665            .ok_or(Error::<T>::NumOverflow)?;1666        let itemcopy = item.clone();16671668        let value = item.owner.first().unwrap().fraction;1669        let owner = item.owner.first().unwrap().owner.clone();16701671        Self::add_token_index(collection_id, current_index, owner.clone())?;16721673        <ItemListIndex>::insert(collection_id, current_index);1674        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);16751676        // Update balance1677        let new_balance = <Balance<T>>::get(collection_id, owner.clone())1678            .checked_add(value)1679            .ok_or(Error::<T>::NumOverflow)?;1680        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);16811682        Ok(())1683    }16841685    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1686        let current_index = <ItemListIndex>::get(collection_id)1687            .checked_add(1)1688            .ok_or(Error::<T>::NumOverflow)?;16891690        let item_owner = item.owner.clone();1691        Self::add_token_index(collection_id, current_index, item.owner.clone())?;16921693        <ItemListIndex>::insert(collection_id, current_index);1694        <NftItemList<T>>::insert(collection_id, current_index, item);16951696        // Update balance1697        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1698            .checked_add(1)1699            .ok_or(Error::<T>::NumOverflow)?;1700        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17011702        Ok(())1703    }17041705    fn burn_refungible_item(1706        collection_id: CollectionId,1707        item_id: TokenId,1708        owner: T::AccountId,1709    ) -> DispatchResult {1710        ensure!(1711            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1712            Error::<T>::TokenNotFound1713        );1714        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1715        let item = collection1716            .owner1717            .iter()1718            .filter(|&i| i.owner == owner)1719            .next()1720            .unwrap();1721        Self::remove_token_index(collection_id, item_id, owner.clone())?;17221723        // update balance1724        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1725            .checked_sub(item.fraction)1726            .ok_or(Error::<T>::NumOverflow)?;1727        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17281729        <ReFungibleItemList<T>>::remove(collection_id, item_id);17301731        Ok(())1732    }17331734    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1735        ensure!(1736            <NftItemList<T>>::contains_key(collection_id, item_id),1737            Error::<T>::TokenNotFound1738        );1739        let item = <NftItemList<T>>::get(collection_id, item_id);1740        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17411742        // update balance1743        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1744            .checked_sub(1)1745            .ok_or(Error::<T>::NumOverflow)?;1746        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1747        <NftItemList<T>>::remove(collection_id, item_id);17481749        Ok(())1750    }17511752    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1753        ensure!(1754            <FungibleItemList<T>>::contains_key(collection_id, owner),1755            Error::<T>::TokenNotFound1756        );1757        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1758        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);17591760        // update balance1761        let new_balance = <Balance<T>>::get(collection_id, owner)1762            .checked_sub(value)1763            .ok_or(Error::<T>::NumOverflow)?;1764        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17651766        if balance.value - value > 0 {1767            balance.value -= value;1768            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1769        }1770        else {1771            <FungibleItemList<T>>::remove(collection_id, owner);1772        }17731774        Ok(())1775    }17761777    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1778        ensure!(1779            <Collection<T>>::contains_key(collection_id),1780            Error::<T>::CollectionNotFound1781        );1782        Ok(())1783    }17841785    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1786        Self::collection_exists(collection_id)?;17871788        let target_collection = <Collection<T>>::get(collection_id);1789        ensure!(1790            subject == target_collection.owner,1791            Error::<T>::NoPermission1792        );17931794        Ok(())1795    }17961797    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1798        let target_collection = <Collection<T>>::get(collection_id);1799        let mut result: bool = subject == target_collection.owner;1800        let exists = <AdminList<T>>::contains_key(collection_id);18011802        if !result & exists {1803            if <AdminList<T>>::get(collection_id).contains(&subject) {1804                result = true1805            }1806        }18071808        result1809    }18101811    fn check_owner_or_admin_permissions(1812        collection_id: CollectionId,1813        subject: T::AccountId,1814    ) -> DispatchResult {1815        Self::collection_exists(collection_id)?;1816        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18171818        ensure!(1819            result,1820            Error::<T>::NoPermission1821        );1822        Ok(())1823    }18241825    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1826        let target_collection = <Collection<T>>::get(collection_id);18271828        match target_collection.mode {1829            CollectionMode::NFT => {1830                <NftItemList<T>>::get(collection_id, item_id).owner == subject1831            }1832            CollectionMode::Fungible(_) => {1833                <FungibleItemList<T>>::contains_key(collection_id, &subject)1834            }1835            CollectionMode::ReFungible(_) => {1836                <ReFungibleItemList<T>>::get(collection_id, item_id)1837                    .owner1838                    .iter()1839                    .any(|i| i.owner == subject)1840            }1841            CollectionMode::Invalid => false,1842        }1843    }18441845    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1846        let mes = Error::<T>::AddresNotInWhiteList;1847        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);18481849        Ok(())1850    }18511852    fn transfer_fungible(1853        collection_id: CollectionId,1854        value: u128,1855        owner: &T::AccountId,1856        recipient: &T::AccountId,1857    ) -> DispatchResult {1858        ensure!(1859            <FungibleItemList<T>>::contains_key(collection_id, owner),1860            Error::<T>::TokenNotFound1861        );18621863        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1864        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);18651866        // Send balance to recipient (updates balanceOf of recipient)1867        Self::add_fungible_item(collection_id, recipient, value)?;18681869        // update balanceOf of sender1870        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);18711872        // Reduce or remove sender1873        if balance.value == value {1874            <FungibleItemList<T>>::remove(collection_id, owner);1875        }1876        else {1877            balance.value -= value;1878            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1879        }18801881        Ok(())1882    }18831884    fn transfer_refungible(1885        collection_id: CollectionId,1886        item_id: TokenId,1887        value: u128,1888        owner: T::AccountId,1889        new_owner: T::AccountId,1890    ) -> DispatchResult {1891        ensure!(1892            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1893            Error::<T>::TokenNotFound1894        );18951896        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1897        let item = full_item1898            .owner1899            .iter()1900            .filter(|i| i.owner == owner)1901            .next()1902            .ok_or(Error::<T>::NumOverflow)?;1903        let amount = item.fraction;19041905        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19061907        // update balance1908        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1909            .checked_sub(value)1910            .ok_or(Error::<T>::NumOverflow)?;1911        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19121913        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1914            .checked_add(value)1915            .ok_or(Error::<T>::NumOverflow)?;1916        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19171918        let old_owner = item.owner.clone();1919        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19201921        // transfer1922        if amount == value && !new_owner_has_account {1923            // change owner1924            // new owner do not have account1925            let mut new_full_item = full_item.clone();1926            new_full_item1927                .owner1928                .iter_mut()1929                .find(|i| i.owner == owner)1930                .unwrap()1931                .owner = new_owner.clone();1932            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19331934            // update index collection1935            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1936        } else {1937            let mut new_full_item = full_item.clone();1938            new_full_item1939                .owner1940                .iter_mut()1941                .find(|i| i.owner == owner)1942                .unwrap()1943                .fraction -= value;19441945            // separate amount1946            if new_owner_has_account {1947                // new owner has account1948                new_full_item1949                    .owner1950                    .iter_mut()1951                    .find(|i| i.owner == new_owner)1952                    .unwrap()1953                    .fraction += value;1954            } else {1955                // new owner do not have account1956                new_full_item.owner.push(Ownership {1957                    owner: new_owner.clone(),1958                    fraction: value,1959                });1960                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1961            }19621963            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1964        }19651966        Ok(())1967    }19681969    fn transfer_nft(1970        collection_id: CollectionId,1971        item_id: TokenId,1972        sender: T::AccountId,1973        new_owner: T::AccountId,1974    ) -> DispatchResult {1975        ensure!(1976            <NftItemList<T>>::contains_key(collection_id, item_id),1977            Error::<T>::TokenNotFound1978        );19791980        let mut item = <NftItemList<T>>::get(collection_id, item_id);19811982        ensure!(1983            sender == item.owner,1984            Error::<T>::MustBeTokenOwner1985        );19861987        // update balance1988        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1989            .checked_sub(1)1990            .ok_or(Error::<T>::NumOverflow)?;1991        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19921993        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1994            .checked_add(1)1995            .ok_or(Error::<T>::NumOverflow)?;1996        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19971998        // change owner1999        let old_owner = item.owner.clone();2000        item.owner = new_owner.clone();2001        <NftItemList<T>>::insert(collection_id, item_id, item);20022003        // update index collection2004        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;20052006        Ok(())2007    }2008    2009    fn item_exists(2010        collection_id: CollectionId,2011        item_id: TokenId,2012        mode: &CollectionMode2013    ) -> DispatchResult {2014        match mode {2015            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2016            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2017            _ => ()2018        };2019        2020        Ok(())2021    }20222023    fn set_re_fungible_variable_data(2024        collection_id: CollectionId,2025        item_id: TokenId,2026        data: Vec<u8>2027    ) -> DispatchResult {2028        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20292030        item.variable_data = data;20312032        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20332034        Ok(())2035    }20362037    fn set_nft_variable_data(2038        collection_id: CollectionId,2039        item_id: TokenId,2040        data: Vec<u8>2041    ) -> DispatchResult {2042        let mut item = <NftItemList<T>>::get(collection_id, item_id);2043        2044        item.variable_data = data;20452046        <NftItemList<T>>::insert(collection_id, item_id, item);2047        2048        Ok(())2049    }20502051    fn init_collection(item: &CollectionType<T::AccountId>) {2052        // check params2053        assert!(2054            item.decimal_points <= MAX_DECIMAL_POINTS,2055            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2056        );2057        assert!(2058            item.name.len() <= 64,2059            "Collection name can not be longer than 63 char"2060        );2061        assert!(2062            item.name.len() <= 256,2063            "Collection description can not be longer than 255 char"2064        );2065        assert!(2066            item.token_prefix.len() <= 16,2067            "Token prefix can not be longer than 15 char"2068        );20692070        // Generate next collection ID2071        let next_id = CreatedCollectionCount::get()2072            .checked_add(1)2073            .unwrap();20742075        CreatedCollectionCount::put(next_id);2076    }20772078    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2079        let current_index = <ItemListIndex>::get(collection_id)2080            .checked_add(1)2081            .unwrap();20822083        let item_owner = item.owner.clone();2084        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();20852086        <ItemListIndex>::insert(collection_id, current_index);20872088        // Update balance2089        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2090            .checked_add(1)2091            .unwrap();2092        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2093    }20942095    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2096        let current_index = <ItemListIndex>::get(collection_id)2097            .checked_add(1)2098            .unwrap();20992100        Self::add_token_index(collection_id, current_index, (*owner).clone()).unwrap();21012102        <ItemListIndex>::insert(collection_id, current_index);21032104        // Update balance2105        let new_balance = <Balance<T>>::get(collection_id, owner)2106            .checked_add(item.value)2107            .unwrap();2108        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2109    }21102111    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2112        let current_index = <ItemListIndex>::get(collection_id)2113            .checked_add(1)2114            .unwrap();21152116        let value = item.owner.first().unwrap().fraction;2117        let owner = item.owner.first().unwrap().owner.clone();21182119        Self::add_token_index(collection_id, current_index, owner.clone()).unwrap();21202121        <ItemListIndex>::insert(collection_id, current_index);21222123        // Update balance2124        let new_balance = <Balance<T>>::get(collection_id, owner.clone())2125            .checked_add(value)2126            .unwrap();2127        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2128    }21292130    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {21312132        // add to account limit2133        if <AccountItemCount<T>>::contains_key(owner.clone()) {21342135            // bound Owned tokens by a single address2136            let count = <AccountItemCount<T>>::get(owner.clone());2137            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21382139            <AccountItemCount<T>>::insert(owner.clone(), count2140                .checked_add(1)2141                .ok_or(Error::<T>::NumOverflow)?);2142        }2143        else {2144            <AccountItemCount<T>>::insert(owner.clone(), 1);2145        }21462147        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2148        if list_exists {2149            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2150            let item_contains = list.contains(&item_index.clone());21512152            if !item_contains {2153                list.push(item_index.clone());2154            }21552156            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2157        } else {2158            let mut itm = Vec::new();2159            itm.push(item_index.clone());2160            <AddressTokens<T>>::insert(collection_id, owner, itm);2161            2162        }21632164        Ok(())2165    }21662167    fn remove_token_index(2168        collection_id: CollectionId,2169        item_index: TokenId,2170        owner: T::AccountId,2171    ) -> DispatchResult {21722173        // update counter2174        <AccountItemCount<T>>::insert(owner.clone(), 2175            <AccountItemCount<T>>::get(owner.clone())2176            .checked_sub(1)2177            .ok_or(Error::<T>::NumOverflow)?);217821792180        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2181        if list_exists {2182            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2183            let item_contains = list.contains(&item_index.clone());21842185            if item_contains {2186                list.retain(|&item| item != item_index);2187                <AddressTokens<T>>::insert(collection_id, owner, list);2188            }2189        }21902191        Ok(())2192    }21932194    fn move_token_index(2195        collection_id: CollectionId,2196        item_index: TokenId,2197        old_owner: T::AccountId,2198        new_owner: T::AccountId,2199    ) -> DispatchResult {2200        Self::remove_token_index(collection_id, item_index, old_owner)?;2201        Self::add_token_index(collection_id, item_index, new_owner)?;22022203        Ok(())2204    }2205    2206    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2207        if <ContractOwner<T>>::contains_key(contract.clone()) {2208            let owner = <ContractOwner<T>>::get(contract);2209            ensure!(account == owner, Error::<T>::NoPermission);2210        } else {2211            fail!(Error::<T>::NoPermission);2212        }22132214        Ok(())2215    }2216}22172218////////////////////////////////////////////////////////////////////////////////////////////////////2219// Economic models2220// #region22212222/// Fee multiplier.2223pub type Multiplier = FixedU128;22242225type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2226    <T as system::Trait>::AccountId,2227>>::Balance;2228type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2229    <T as system::Trait>::AccountId,2230>>::NegativeImbalance;22312232/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2233/// in the queue.2234#[derive(Encode, Decode, Clone, Eq, PartialEq)]2235pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2236    #[codec(compact)] BalanceOf<T>2237);22382239impl<T: Trait + Send + Sync> sp_std::fmt::Debug2240    for ChargeTransactionPayment<T>2241{2242    #[cfg(feature = "std")]2243    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2244        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2245    }2246    #[cfg(not(feature = "std"))]2247    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2248        Ok(())2249    }2250}22512252impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2253where2254    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2255    BalanceOf<T>: Send + Sync + FixedPointOperand,2256{2257    /// utility constructor. Used only in client/factory code.2258    pub fn from(fee: BalanceOf<T>) -> Self {2259        Self(fee)2260    }22612262    pub fn traditional_fee(2263        len: usize,2264        info: &DispatchInfoOf<T::Call>,2265        tip: BalanceOf<T>,2266    ) -> BalanceOf<T>2267    where2268        T::Call: Dispatchable<Info = DispatchInfo>,2269    {2270        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2271    }22722273	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2274		let weight_saturation = T::MaximumBlockWeight::get() / info.weight.max(1);2275		let len_saturation = T::MaximumBlockLength::get() as u64 / (len as u64).max(1);2276		let coefficient: BalanceOf<T> = weight_saturation.min(len_saturation).saturated_into::<BalanceOf<T>>();2277		final_fee.saturating_mul(coefficient).saturated_into::<TransactionPriority>()2278	}22792280    fn withdraw_fee(2281        &self,2282        who: &T::AccountId,2283        call: &T::Call,2284        info: &DispatchInfoOf<T::Call>,2285        len: usize,2286    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2287        let tip = self.0;22882289        // Set fee based on call type. Creating collection costs 1 Unique.2290        // All other transactions have traditional fees so far2291        // let fee = match call.is_sub_type() {2292        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2293        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2294        //                                                 // _ => <BalanceOf<T>>::from(100)2295        // };2296        let fee = Self::traditional_fee(len, info, tip);22972298        // Only mess with balances if fee is not zero.2299        if fee.is_zero() {2300            return Ok((fee, None));2301        }23022303        // Determine who is paying transaction fee based on ecnomic model2304        // Parse call to extract collection ID and access collection sponsor2305        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2306            Some(Call::create_item(collection_id, _owner, _properties)) => {23072308                // check free create limit2309                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2310                   (<Collection<T>>::get(collection_id).sponsor_confirmed)2311                {2312                    <Collection<T>>::get(collection_id).sponsor2313                } else {2314                    T::AccountId::default()2315                }2316            }2317            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2318                2319                let mut sponsor_transfer = false;2320                if <Collection<T>>::get(collection_id).sponsor_confirmed {23212322                    let collection_limits = <Collection<T>>::get(collection_id).limits;2323                    let collection_mode = <Collection<T>>::get(collection_id).mode;2324    2325                    // sponsor timeout2326                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2327                    sponsor_transfer = match collection_mode {2328                        CollectionMode::NFT => {2329    2330                            // get correct limit2331                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2332                                collection_limits.sponsor_transfer_timeout2333                            } else {2334                                ChainLimit::get().nft_sponsor_transfer_timeout2335                            };2336    2337                            let mut sponsored = true;2338                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2339                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2340                                let limit_time = last_tx_block + limit.into();2341                                if block_number <= limit_time {2342                                    sponsored = false;2343                                }2344                            }2345                            if sponsored {2346                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2347                            }23482349                            sponsored2350                        }2351                        CollectionMode::Fungible(_) => {2352    2353                            // get correct limit2354                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2355                                collection_limits.sponsor_transfer_timeout2356                            } else {2357                                ChainLimit::get().fungible_sponsor_transfer_timeout2358                            };2359    2360                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2361                            let mut sponsored = true;2362                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2363                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2364                                let limit_time = last_tx_block + limit.into();2365                                if block_number <= limit_time {2366                                    sponsored = false;2367                                }2368                            }2369                            if sponsored {2370                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2371                            }23722373                            sponsored2374                        }2375                        CollectionMode::ReFungible(_) => {2376    2377                            // get correct limit2378                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2379                                collection_limits.sponsor_transfer_timeout2380                            } else {2381                                ChainLimit::get().refungible_sponsor_transfer_timeout2382                            };2383    2384                            let mut sponsored = true;2385                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2386                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2387                                let limit_time = last_tx_block + limit.into();2388                                if block_number <= limit_time {2389                                    sponsored = false;2390                                }2391                            }2392                            if sponsored {2393                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2394                            }23952396                            sponsored2397                        }2398                        _ => {2399                            false2400                        },2401                    };2402                }24032404                if !sponsor_transfer {2405                    T::AccountId::default()2406                } else {2407                    <Collection<T>>::get(collection_id).sponsor2408                }2409            }24102411            _ => T::AccountId::default(),2412        };24132414        // Sponsor smart contracts2415        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {24162417            // On instantiation: set the contract owner2418            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {24192420                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2421                    code_hash,2422                    &data,2423                    &who,2424                );2425                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());24262427                T::AccountId::default()2428            },24292430            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2431            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {24322433                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());24342435                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2436                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2437                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2438                  2439                if !owned_contract && white_list_enabled {2440                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2441                        return Err(InvalidTransaction::Call.into());2442                    }2443                }24442445                let mut sponsor_transfer = false;2446                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2447                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2448                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2449                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2450                    let limit_time = last_tx_block + rate_limit;24512452                    if block_number >= limit_time {2453                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2454                        sponsor_transfer = true;2455                    }2456                } else {2457                    sponsor_transfer = false;2458                }2459               2460                2461                let mut sp = T::AccountId::default();2462                if sponsor_transfer {2463                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2464                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2465                            sp = called_contract;2466                        }2467                    }2468                }24692470                sp2471            },24722473            _ => sponsor,2474        };24752476        let mut who_pays_fee: T::AccountId = sponsor.clone();2477        if sponsor == T::AccountId::default() {2478            who_pays_fee = who.clone();2479        }24802481        match <T as transaction_payment::Trait>::Currency::withdraw(2482            &who_pays_fee,2483            fee,2484            if tip.is_zero() {2485                WithdrawReason::TransactionPayment.into()2486            } else {2487                WithdrawReason::TransactionPayment | WithdrawReason::Tip2488            },2489            ExistenceRequirement::KeepAlive,2490        ) {2491            Ok(imbalance) => Ok((fee, Some(imbalance))),2492            Err(_) => Err(InvalidTransaction::Payment.into()),2493        }2494    }2495}249624972498impl<T: Trait + Send + Sync> SignedExtension2499    for ChargeTransactionPayment<T>2500where2501    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2502    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2503{2504    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2505    type AccountId = T::AccountId;2506    type Call = T::Call;2507    type AdditionalSigned = ();2508    type Pre = (2509        BalanceOf<T>,2510        Self::AccountId,2511        Option<NegativeImbalanceOf<T>>,2512        BalanceOf<T>,2513    );2514    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2515        Ok(())2516    }25172518    fn validate(2519        &self,2520        who: &Self::AccountId,2521        call: &Self::Call,2522        info: &DispatchInfoOf<Self::Call>,2523        len: usize,2524    ) -> TransactionValidity {2525		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2526		Ok(ValidTransaction {2527			priority: Self::get_priority(len, info, fee),2528			..Default::default()2529		})2530    }25312532    fn pre_dispatch(2533        self,2534        who: &Self::AccountId,2535        call: &Self::Call,2536        info: &DispatchInfoOf<Self::Call>,2537        len: usize,2538    ) -> Result<Self::Pre, TransactionValidityError> {2539        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2540        Ok((self.0, who.clone(), imbalance, fee))2541    }25422543    fn post_dispatch(2544        pre: Self::Pre,2545        info: &DispatchInfoOf<Self::Call>,2546        post_info: &PostDispatchInfoOf<Self::Call>,2547        len: usize,2548        _result: &DispatchResult,2549    ) -> Result<(), TransactionValidityError> {2550        let (tip, who, imbalance, fee) = pre;2551        if let Some(payed) = imbalance {2552            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2553                len as u32, info, post_info, tip,2554            );2555            let refund = fee.saturating_sub(actual_fee);2556            let actual_payment =2557                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2558                    &who, refund,2559                ) {2560                    Ok(refund_imbalance) => {2561                        // The refund cannot be larger than the up front payed max weight.2562                        // `PostDispatchInfo::calc_unspent` guards against such a case.2563                        match payed.offset(refund_imbalance) {2564                            Ok(actual_payment) => actual_payment,2565                            Err(_) => return Err(InvalidTransaction::Payment.into()),2566                        }2567                    }2568                    // We do not recreate the account using the refund. The up front payment2569                    // is gone in that case.2570                    Err(_) => payed,2571                };2572            let imbalances = actual_payment.split(tip);2573            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2574                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2575            );2576        }2577        Ok(())2578    }2579}25802581// #endregion
after · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, WithdrawReason,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial,29    },30    IsSubType, StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use pallet_contracts::ContractAddressFor;45use sp_runtime::traits::StaticLookup;4647#[cfg(test)]48mod mock;4950#[cfg(test)]51mod tests;5253mod default_weights;5455pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;56pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;57pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;5859// Structs60// #region6162pub type CollectionId = u32;63pub type TokenId = u32;64pub type DecimalPoints = u8;6566#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]67#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]68pub enum CollectionMode {69    Invalid,70    NFT,71    // decimal points72    Fungible(DecimalPoints),73    // decimal points74    ReFungible(DecimalPoints),75}7677impl Into<u8> for CollectionMode {78    fn into(self) -> u8 {79        match self {80            CollectionMode::Invalid => 0,81            CollectionMode::NFT => 1,82            CollectionMode::Fungible(_) => 2,83            CollectionMode::ReFungible(_) => 3,84        }85    }86}8788#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]89#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]90pub enum AccessMode {91    Normal,92    WhiteList,93}94impl Default for AccessMode {95    fn default() -> Self {96        Self::Normal97    }98}99100impl Default for CollectionMode {101    fn default() -> Self {102        Self::Invalid103    }104}105106#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]107#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]108pub enum SchemaVersion {109    ImageURL,110    Unique,111}112impl Default for SchemaVersion {113    fn default() -> Self {114        Self::ImageURL115    }116}117118#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]119#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]120pub struct Ownership<AccountId> {121    pub owner: AccountId,122    pub fraction: u128,123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct CollectionType<AccountId> {128    pub owner: AccountId,129    pub mode: CollectionMode,130    pub access: AccessMode,131    pub decimal_points: DecimalPoints,132    pub name: Vec<u16>,        // 64 include null escape char133    pub description: Vec<u16>, // 256 include null escape char134    pub token_prefix: Vec<u8>, // 16 include null escape char135    pub mint_mode: bool,136    pub offchain_schema: Vec<u8>,137    pub schema_version: SchemaVersion,138    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender139    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.140    pub limits: CollectionLimits, // Collection private restrictions 141    pub variable_on_chain_schema: Vec<u8>, //142    pub const_on_chain_schema: Vec<u8>, //143}144145#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]146#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]147pub struct NftItemType<AccountId> {148    pub owner: AccountId,149    pub const_data: Vec<u8>,150    pub variable_data: Vec<u8>,151}152153#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]154#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]155pub struct FungibleItemType {156    pub value: u128,157}158159#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]160#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]161pub struct ReFungibleItemType<AccountId> {162    pub owner: Vec<Ownership<AccountId>>,163    pub const_data: Vec<u8>,164    pub variable_data: Vec<u8>,165}166167// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]168// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169// pub struct VestingItem<AccountId, Moment> {170//     pub sender: AccountId,171//     pub recipient: AccountId,172//     pub collection_id: CollectionId,173//     pub item_id: TokenId,174//     pub amount: u64,175//     pub vesting_date: Moment,176// }177178#[derive(Encode, Decode, Debug, Clone, PartialEq)]179#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]180pub struct CollectionLimits {181    pub account_token_ownership_limit: u32,182    pub sponsored_data_size: u32,183    pub token_limit: u32,184185    // Timeouts for item types in passed blocks186    pub sponsor_transfer_timeout: u32,187}188189impl Default for CollectionLimits {190    fn default() -> CollectionLimits {191        CollectionLimits { 192            account_token_ownership_limit: 10_000_000, 193            token_limit: u32::max_value(),194            sponsored_data_size: u32::max_value(), 195            sponsor_transfer_timeout: 14400 }196    }197}198199#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]200#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]201pub struct ChainLimits {202    pub collection_numbers_limit: u32,203    pub account_token_ownership_limit: u32,204    pub collections_admins_limit: u64,205    pub custom_data_limit: u32,206207    // Timeouts for item types in passed blocks208    pub nft_sponsor_transfer_timeout: u32,209    pub fungible_sponsor_transfer_timeout: u32,210    pub refungible_sponsor_transfer_timeout: u32,211}212213pub trait WeightInfo {214	fn create_collection() -> Weight;215	fn destroy_collection() -> Weight;216	fn add_to_white_list() -> Weight;217	fn remove_from_white_list() -> Weight;218    fn set_public_access_mode() -> Weight;219    fn set_mint_permission() -> Weight;220    fn change_collection_owner() -> Weight;221    fn add_collection_admin() -> Weight;222    fn remove_collection_admin() -> Weight;223    fn set_collection_sponsor() -> Weight;224    fn confirm_sponsorship() -> Weight;225    fn remove_collection_sponsor() -> Weight;226    fn create_item(s: usize) -> Weight;227    fn burn_item() -> Weight;228    fn transfer() -> Weight;229    fn approve() -> Weight;230    fn transfer_from() -> Weight;231    fn set_offchain_schema() -> Weight;232    fn set_const_on_chain_schema() -> Weight;233    fn set_variable_on_chain_schema() -> Weight;234    fn set_variable_meta_data() -> Weight;235    fn enable_contract_sponsoring() -> Weight;236    fn set_schema_version() -> Weight;237    fn set_chain_limits() -> Weight;238    fn set_contract_sponsoring_rate_limit() -> Weight;239    fn toggle_contract_white_list() -> Weight;240    fn add_to_contract_white_list() -> Weight;241    fn remove_from_contract_white_list() -> Weight;242    fn set_collection_limits() -> Weight;243}244245#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]246#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]247pub struct CreateNftData {248    pub const_data: Vec<u8>,249    pub variable_data: Vec<u8>,250}251252#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]253#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]254pub struct CreateFungibleData {255    pub value: u128,256}257258#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]259#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]260pub struct CreateReFungibleData {261    pub const_data: Vec<u8>,262    pub variable_data: Vec<u8>,263}264265#[derive(Encode, Decode, Debug, Clone, PartialEq)]266#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]267pub enum CreateItemData {268    NFT(CreateNftData),269    Fungible(CreateFungibleData),270    ReFungible(CreateReFungibleData),271}272273impl CreateItemData {274    pub fn len(&self) -> usize {275        let len = match self {276            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),277            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),278            _ => 0279        };280        281        return len;282    }283}284285impl From<CreateNftData> for CreateItemData {286    fn from(item: CreateNftData) -> Self {287        CreateItemData::NFT(item)288    }289}290291impl From<CreateReFungibleData> for CreateItemData {292    fn from(item: CreateReFungibleData) -> Self {293        CreateItemData::ReFungible(item)294    }295}296297impl From<CreateFungibleData> for CreateItemData {298    fn from(item: CreateFungibleData) -> Self {299        CreateItemData::Fungible(item)300    }301}302303304decl_error! {305	/// Error for non-fungible-token module.306	pub enum Error for Module<T: Trait> {307        /// Total collections bound exceeded.308        TotalCollectionsLimitExceeded,309		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.310        CollectionDecimalPointLimitExceeded, 311        /// Collection name can not be longer than 63 char.312        CollectionNameLimitExceeded, 313        /// Collection description can not be longer than 255 char.314        CollectionDescriptionLimitExceeded, 315        /// Token prefix can not be longer than 15 char.316        CollectionTokenPrefixLimitExceeded,317        /// This collection does not exist.318        CollectionNotFound,319        /// Item not exists.320        TokenNotFound,321        /// Arithmetic calculation overflow.322        NumOverflow,       323        /// Account already has admin role.324        AlreadyAdmin,  325        /// You do not own this collection.326        NoPermission,327        /// This address is not set as sponsor, use setCollectionSponsor first.328        ConfirmUnsetSponsorFail,329        /// Collection is not in mint mode.330        PublicMintingNotAllowed,331        /// Sender parameter and item owner must be equal.332        MustBeTokenOwner,333        /// Item balance not enough.334        TokenValueTooLow,335        /// Size of item is too large.336        NftSizeLimitExceeded,337        /// No approve found338        ApproveNotFound,339        /// Requested value more than approved.340        TokenValueNotEnough,341        /// Only approved addresses can call this method.342        ApproveRequired,343        /// Address is not in white list.344        AddresNotInWhiteList,345        /// Number of collection admins bound exceeded.346        CollectionAdminsLimitExceeded,347        /// Owned tokens by a single address bound exceeded.348        AddressOwnershipLimitExceeded,349        /// Length of items properties must be greater than 0.350        EmptyArgument,351        /// const_data exceeded data limit.352        TokenConstDataLimitExceeded,353        /// variable_data exceeded data limit.354        TokenVariableDataLimitExceeded,355        /// Not NFT item data used to mint in NFT collection.356        NotNftDataUsedToMintNftCollectionToken,357        /// Not Fungible item data used to mint in Fungible collection.358        NotFungibleDataUsedToMintFungibleCollectionToken,359        /// Not Re Fungible item data used to mint in Re Fungible collection.360        NotReFungibleDataUsedToMintReFungibleCollectionToken,361        /// Unexpected collection type.362        UnexpectedCollectionType,363        /// Can't store metadata in fungible tokens.364        CantStoreMetadataInFungibleTokens,365        /// Collection token limit exceeded366        CollectionTokenLimitExceeded,367        /// Account token limit exceeded per collection368        AccountTokenLimitExceeded,369        /// Collection limit bounds per collection exceeded370        CollectionLimitBoundsExceeded371	}372}373374pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {375    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;376377    /// Weight information for extrinsics in this pallet.378	type WeightInfo: WeightInfo;379}380381#[cfg(feature = "runtime-benchmarks")]382mod benchmarking;383384// #endregion385386decl_storage! {387    trait Store for Module<T: Trait> as Nft {388389        // Private members390        NextCollectionID: CollectionId;391        CreatedCollectionCount: u32;392        ChainVersion: u64;393        ItemListIndex: map hasher(identity) CollectionId => TokenId;394395        // Chain limits struct396        pub ChainLimit get(fn chain_limit) config(): ChainLimits;397398        // Bound counters399        CollectionCount: u32;400        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;401402        // Basic collections403        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;404        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;405        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;406407        /// Balance owner per collection map408        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;409410        /// second parameter: item id + owner account id + spender account id411        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;412413        /// Item collections414        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;415        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;416        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;417418        /// Index list419        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;420421        /// Tokens transfer baskets422        pub CreateItemBasket get(fn create_item_basket): map hasher(identity) CollectionId => T::BlockNumber;423        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;424        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;425        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;426427        // Contract Sponsorship and Ownership428        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;429        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;430        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;431        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;432        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 433        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 434    }435    add_extra_genesis {436        build(|config: &GenesisConfig<T>| {437            // Modification of storage438            for (_num, _c) in &config.collection {439                <Module<T>>::init_collection(_c);440            }441442            for (_num, _c, _i) in &config.nft_item_id {443                <Module<T>>::init_nft_token(*_c, _i);444            }445446            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {447                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);448            }449450            for (_num, _c, _i) in &config.refungible_item_id {451                <Module<T>>::init_refungible_token(*_c, _i);452            }453        })454    }455}456457decl_event!(458    pub enum Event<T>459    where460        AccountId = <T as system::Trait>::AccountId,461    {462        /// New collection was created463        /// 464        /// # Arguments465        /// 466        /// * collection_id: Globally unique identifier of newly created collection.467        /// 468        /// * mode: [CollectionMode] converted into u8.469        /// 470        /// * account_id: Collection owner.471        Created(CollectionId, u8, AccountId),472473        /// New item was created.474        /// 475        /// # Arguments476        /// 477        /// * collection_id: Id of the collection where item was created.478        /// 479        /// * item_id: Id of an item. Unique within the collection.480        ItemCreated(CollectionId, TokenId),481482        /// Collection item was burned.483        /// 484        /// # Arguments485        /// 486        /// collection_id.487        /// 488        /// item_id: Identifier of burned NFT.489        ItemDestroyed(CollectionId, TokenId),490    }491);492493decl_module! {494    pub struct Module<T: Trait> for enum Call where origin: T::Origin {495496        fn deposit_event() = default;497        type Error = Error<T>;498499        fn on_initialize(now: T::BlockNumber) -> Weight {500501            if ChainVersion::get() < 2502            {503                let value = NextCollectionID::get();504                CreatedCollectionCount::put(value);505                ChainVersion::put(2);506            }507508            0509        }510511        /// 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.512        /// 513        /// # Permissions514        /// 515        /// * Anyone.516        /// 517        /// # Arguments518        /// 519        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.520        /// 521        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.522        /// 523        /// * token_prefix: UTF-8 string with token prefix.524        /// 525        /// * mode: [CollectionMode] collection type and type dependent data.526        // returns collection ID527        #[weight = T::WeightInfo::create_collection()]528        pub fn create_collection(origin,529                                 collection_name: Vec<u16>,530                                 collection_description: Vec<u16>,531                                 token_prefix: Vec<u8>,532                                 mode: CollectionMode) -> DispatchResult {533534            // Anyone can create a collection535            let who = ensure_signed(origin)?;536537            let decimal_points = match mode {538                CollectionMode::Fungible(points) => points,539                CollectionMode::ReFungible(points) => points,540                _ => 0541            };542543            // bound Total number of collections544            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);545546            // check params547            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);548            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);549            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);550            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);551552            // Generate next collection ID553            let next_id = CreatedCollectionCount::get()554                .checked_add(1)555                .ok_or(Error::<T>::NumOverflow)?;556557            // bound counter558            let total = CollectionCount::get()559                .checked_add(1)560                .ok_or(Error::<T>::NumOverflow)?;561562            CreatedCollectionCount::put(next_id);563            CollectionCount::put(total);564565            // Create new collection566            let new_collection = CollectionType {567                owner: who.clone(),568                name: collection_name,569                mode: mode.clone(),570                mint_mode: false,571                access: AccessMode::Normal,572                description: collection_description,573                decimal_points: decimal_points,574                token_prefix: token_prefix,575                offchain_schema: Vec::new(),576                schema_version: SchemaVersion::ImageURL,577                sponsor: T::AccountId::default(),578                sponsor_confirmed: false,579                variable_on_chain_schema: Vec::new(),580                const_on_chain_schema: Vec::new(),581                limits: CollectionLimits::default(),582            };583584            // Add new collection to map585            <Collection<T>>::insert(next_id, new_collection);586587            // call event588            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));589590            Ok(())591        }592593        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.594        /// 595        /// # Permissions596        /// 597        /// * Collection Owner.598        /// 599        /// # Arguments600        /// 601        /// * collection_id: collection to destroy.602        #[weight = T::WeightInfo::destroy_collection()]603        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {604605            let sender = ensure_signed(origin)?;606            Self::check_owner_permissions(collection_id, sender)?;607608            <AddressTokens<T>>::remove_prefix(collection_id);609            <Allowances<T>>::remove_prefix(collection_id);610            <Balance<T>>::remove_prefix(collection_id);611            <ItemListIndex>::remove(collection_id);612            <AdminList<T>>::remove(collection_id);613            <Collection<T>>::remove(collection_id);614            <WhiteList<T>>::remove_prefix(collection_id);615616            <NftItemList<T>>::remove_prefix(collection_id);617            <FungibleItemList<T>>::remove_prefix(collection_id);618            <ReFungibleItemList<T>>::remove_prefix(collection_id);619620            <NftTransferBasket<T>>::remove_prefix(collection_id);621            <FungibleTransferBasket<T>>::remove_prefix(collection_id);622            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);623624            if CollectionCount::get() > 0625            {626                // bound couter627                let total = CollectionCount::get()628                    .checked_sub(1)629                    .ok_or(Error::<T>::NumOverflow)?;630631                CollectionCount::put(total);632            }633634            Ok(())635        }636637        /// Add an address to white list.638        /// 639        /// # Permissions640        /// 641        /// * Collection Owner642        /// * Collection Admin643        /// 644        /// # Arguments645        /// 646        /// * collection_id.647        /// 648        /// * address.649        #[weight = T::WeightInfo::add_to_white_list()]650        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{651652            let sender = ensure_signed(origin)?;653            Self::check_owner_or_admin_permissions(collection_id, sender)?;654655            <WhiteList<T>>::insert(collection_id, address, true);656            657            Ok(())658        }659660        /// Remove an address from white list.661        /// 662        /// # Permissions663        /// 664        /// * Collection Owner665        /// * Collection Admin666        /// 667        /// # Arguments668        /// 669        /// * collection_id.670        /// 671        /// * address.672        #[weight = T::WeightInfo::remove_from_white_list()]673        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{674675            let sender = ensure_signed(origin)?;676            Self::check_owner_or_admin_permissions(collection_id, sender)?;677678            <WhiteList<T>>::remove(collection_id, address);679680            Ok(())681        }682683        /// Toggle between normal and white list access for the methods with access for `Anyone`.684        /// 685        /// # Permissions686        /// 687        /// * Collection Owner.688        /// 689        /// # Arguments690        /// 691        /// * collection_id.692        /// 693        /// * mode: [AccessMode]694        #[weight = T::WeightInfo::set_public_access_mode()]695        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult696        {697            let sender = ensure_signed(origin)?;698699            Self::check_owner_permissions(collection_id, sender)?;700            let mut target_collection = <Collection<T>>::get(collection_id);701            target_collection.access = mode;702            <Collection<T>>::insert(collection_id, target_collection);703704            Ok(())705        }706707        /// Allows Anyone to create tokens if:708        /// * White List is enabled, and709        /// * Address is added to white list, and710        /// * This method was called with True parameter711        /// 712        /// # Permissions713        /// * Collection Owner714        ///715        /// # Arguments716        /// 717        /// * collection_id.718        /// 719        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.720        #[weight = T::WeightInfo::set_mint_permission()]721        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult722        {723            let sender = ensure_signed(origin)?;724725            Self::check_owner_permissions(collection_id, sender)?;726            let mut target_collection = <Collection<T>>::get(collection_id);727            target_collection.mint_mode = mint_permission;728            <Collection<T>>::insert(collection_id, target_collection);729730            Ok(())731        }732733        /// Change the owner of the collection.734        /// 735        /// # Permissions736        /// 737        /// * Collection Owner.738        /// 739        /// # Arguments740        /// 741        /// * collection_id.742        /// 743        /// * new_owner.744        #[weight = T::WeightInfo::change_collection_owner()]745        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {746747            let sender = ensure_signed(origin)?;748            Self::check_owner_permissions(collection_id, sender)?;749            let mut target_collection = <Collection<T>>::get(collection_id);750            target_collection.owner = new_owner;751            <Collection<T>>::insert(collection_id, target_collection);752753            Ok(())754        }755756        /// Adds an admin of the Collection.757        /// 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. 758        /// 759        /// # Permissions760        /// 761        /// * Collection Owner.762        /// * Collection Admin.763        /// 764        /// # Arguments765        /// 766        /// * collection_id: ID of the Collection to add admin for.767        /// 768        /// * new_admin_id: Address of new admin to add.769        #[weight = T::WeightInfo::add_collection_admin()]770        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {771772            let sender = ensure_signed(origin)?;773            Self::check_owner_or_admin_permissions(collection_id, sender)?;774            let mut admin_arr: Vec<T::AccountId> = Vec::new();775776            if <AdminList<T>>::contains_key(collection_id)777            {778                admin_arr = <AdminList<T>>::get(collection_id);779                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);780            }781782            // Number of collection admins783            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);784785            admin_arr.push(new_admin_id);786            <AdminList<T>>::insert(collection_id, admin_arr);787788            Ok(())789        }790791        /// 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.792        ///793        /// # Permissions794        /// 795        /// * Collection Owner.796        /// * Collection Admin.797        /// 798        /// # Arguments799        /// 800        /// * collection_id: ID of the Collection to remove admin for.801        /// 802        /// * account_id: Address of admin to remove.803        #[weight = T::WeightInfo::remove_collection_admin()]804        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {805806            let sender = ensure_signed(origin)?;807            Self::check_owner_or_admin_permissions(collection_id, sender)?;808809            if <AdminList<T>>::contains_key(collection_id)810            {811                let mut admin_arr = <AdminList<T>>::get(collection_id);812                admin_arr.retain(|i| *i != account_id);813                <AdminList<T>>::insert(collection_id, admin_arr);814            }815816            Ok(())817        }818819        /// # Permissions820        /// 821        /// * Collection Owner822        /// 823        /// # Arguments824        /// 825        /// * collection_id.826        /// 827        /// * new_sponsor.828        #[weight = T::WeightInfo::set_collection_sponsor()]829        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {830831            let sender = ensure_signed(origin)?;832            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);833834            let mut target_collection = <Collection<T>>::get(collection_id);835            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);836837            target_collection.sponsor = new_sponsor;838            target_collection.sponsor_confirmed = false;839            <Collection<T>>::insert(collection_id, target_collection);840841            Ok(())842        }843844        /// # Permissions845        /// 846        /// * Sponsor.847        /// 848        /// # Arguments849        /// 850        /// * collection_id.851        #[weight = T::WeightInfo::confirm_sponsorship()]852        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {853854            let sender = ensure_signed(origin)?;855            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);856857            let mut target_collection = <Collection<T>>::get(collection_id);858            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);859860            target_collection.sponsor_confirmed = true;861            <Collection<T>>::insert(collection_id, target_collection);862863            Ok(())864        }865866        /// Switch back to pay-per-own-transaction model.867        ///868        /// # Permissions869        ///870        /// * Collection owner.871        /// 872        /// # Arguments873        /// 874        /// * collection_id.875        #[weight = T::WeightInfo::remove_collection_sponsor()]876        pub fn remove_collection_sponsor(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.owner, Error::<T>::NoPermission);883884            target_collection.sponsor = T::AccountId::default();885            target_collection.sponsor_confirmed = false;886            <Collection<T>>::insert(collection_id, target_collection);887888            Ok(())889        }890891        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.892        /// 893        /// # Permissions894        /// 895        /// * Collection Owner.896        /// * Collection Admin.897        /// * Anyone if898        ///     * White List is enabled, and899        ///     * Address is added to white list, and900        ///     * MintPermission is enabled (see SetMintPermission method)901        /// 902        /// # Arguments903        /// 904        /// * collection_id: ID of the collection.905        /// 906        /// * owner: Address, initial owner of the NFT.907        ///908        /// * data: Token data to store on chain.909        // #[weight =910        // (130_000_000 as Weight)911        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))912        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))913        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]914915        #[weight = T::WeightInfo::create_item(data.len())]916        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {917918            let sender = ensure_signed(origin)?;919920            Self::collection_exists(collection_id)?;921922            let target_collection = <Collection<T>>::get(collection_id);923924            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;925            Self::validate_create_item_args(&target_collection, &data)?;926            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;927928            Ok(())929        }930931        /// This method creates multiple instances of NFT Collection created with CreateCollection method.932        /// 933        /// # Permissions934        /// 935        /// * Collection Owner.936        /// * Collection Admin.937        /// * Anyone if938        ///     * White List is enabled, and939        ///     * Address is added to white list, and940        ///     * MintPermission is enabled (see SetMintPermission method)941        /// 942        /// # Arguments943        /// 944        /// * collection_id: ID of the collection.945        /// 946        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].947        /// 948        /// * owner: Address, initial owner of the NFT.949        #[weight = T::WeightInfo::create_item(items_data.into_iter()950                               .map(|data| { data.len() })951                               .sum())]952        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {953954            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);955            let sender = ensure_signed(origin)?;956957            Self::collection_exists(collection_id)?;958            let target_collection = <Collection<T>>::get(collection_id);959960            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;961962            for data in &items_data {963                Self::validate_create_item_args(&target_collection, data)?;964            }965            for data in &items_data {966                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;967            }968969            Ok(())970        }971972        /// Destroys a concrete instance of NFT.973        /// 974        /// # Permissions975        /// 976        /// * Collection Owner.977        /// * Collection Admin.978        /// * Current NFT Owner.979        /// 980        /// # Arguments981        /// 982        /// * collection_id: ID of the collection.983        /// 984        /// * item_id: ID of NFT to burn.985        #[weight = T::WeightInfo::burn_item()]986        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {987988            let sender = ensure_signed(origin)?;989            Self::collection_exists(collection_id)?;990991            // Transfer permissions check992            let target_collection = <Collection<T>>::get(collection_id);993            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||994                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),995                Error::<T>::NoPermission);996997            if target_collection.access == AccessMode::WhiteList {998                Self::check_white_list(collection_id, &sender)?;999            }10001001            match target_collection.mode1002            {1003                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1004                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1005                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1006                _ => ()1007            };10081009            // call event1010            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10111012            Ok(())1013        }10141015        /// Change ownership of the token.1016        /// 1017        /// # Permissions1018        /// 1019        /// * Collection Owner1020        /// * Collection Admin1021        /// * Current NFT owner1022        ///1023        /// # Arguments1024        /// 1025        /// * recipient: Address of token recipient.1026        /// 1027        /// * collection_id.1028        /// 1029        /// * item_id: ID of the item1030        ///     * Non-Fungible Mode: Required.1031        ///     * Fungible Mode: Ignored.1032        ///     * Re-Fungible Mode: Required.1033        /// 1034        /// * value: Amount to transfer.1035        ///     * Non-Fungible Mode: Ignored1036        ///     * Fungible Mode: Must specify transferred amount1037        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1038        #[weight = T::WeightInfo::transfer()]1039        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10401041            let sender = ensure_signed(origin)?;1042            let target_collection = <Collection<T>>::get(collection_id);10431044            // Limits check1045            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;10461047            // Transfer permissions check1048            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1049                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1050                Error::<T>::NoPermission);10511052            if target_collection.access == AccessMode::WhiteList {1053                Self::check_white_list(collection_id, &sender)?;1054                Self::check_white_list(collection_id, &recipient)?;1055            }10561057            match target_collection.mode1058            {1059                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1060                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1061                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1062                _ => ()1063            };10641065            Ok(())1066        }10671068        /// Set, change, or remove approved address to transfer the ownership of the NFT.1069        /// 1070        /// # Permissions1071        /// 1072        /// * Collection Owner1073        /// * Collection Admin1074        /// * Current NFT owner1075        /// 1076        /// # Arguments1077        /// 1078        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1079        /// 1080        /// * collection_id.1081        /// 1082        /// * item_id: ID of the item.1083        #[weight = T::WeightInfo::approve()]1084        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {10851086            let sender = ensure_signed(origin)?;10871088            // Transfer permissions check1089            let target_collection = <Collection<T>>::get(collection_id);1090            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1091                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1092                Error::<T>::NoPermission);10931094            if target_collection.access == AccessMode::WhiteList {1095                Self::check_white_list(collection_id, &sender)?;1096                Self::check_white_list(collection_id, &spender)?;1097            }10981099            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1100            let mut allowance: u128 = amount;1101            if allowance_exists {1102                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1103            }1104            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11051106            Ok(())1107        }1108        1109        /// 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.1110        /// 1111        /// # Permissions1112        /// * Collection Owner1113        /// * Collection Admin1114        /// * Current NFT owner1115        /// * Address approved by current NFT owner1116        /// 1117        /// # Arguments1118        /// 1119        /// * from: Address that owns token.1120        /// 1121        /// * recipient: Address of token recipient.1122        /// 1123        /// * collection_id.1124        /// 1125        /// * item_id: ID of the item.1126        /// 1127        /// * value: Amount to transfer.1128        #[weight = T::WeightInfo::transfer_from()]1129        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11301131            let sender = ensure_signed(origin)?;1132            let mut appoved_transfer = false;11331134            // Check approval1135            let mut approval: u128 = 0;1136            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &recipient)) {1137                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &recipient));1138                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1139                appoved_transfer = true;1140            }11411142            let target_collection = <Collection<T>>::get(collection_id);11431144            // Limits check1145            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11461147            // Transfer permissions check         1148            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1149                Error::<T>::NoPermission);11501151            if target_collection.access == AccessMode::WhiteList {1152                Self::check_white_list(collection_id, &sender)?;1153                Self::check_white_list(collection_id, &recipient)?;1154            }11551156            // Reduce approval by transferred amount or remove if remaining approval drops to 01157            if approval - value > 0 {1158                <Allowances<T>>::insert(collection_id, (item_id, &from, &recipient), approval - value);1159            }1160            else {1161                <Allowances<T>>::remove(collection_id, (item_id, &from, &recipient));1162            }11631164            match target_collection.mode1165            {1166                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1167                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1168                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1169                _ => ()1170            };11711172            Ok(())1173        }11741175        #[weight = 0]1176        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11771178            // let no_perm_mes = "You do not have permissions to modify this collection";1179            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1180            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1181            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11821183            // // on_nft_received  call11841185            // Self::transfer(origin, collection_id, item_id, new_owner)?;11861187            Ok(())1188        }11891190        /// Set off-chain data schema.1191        /// 1192        /// # Permissions1193        /// 1194        /// * Collection Owner1195        /// * Collection Admin1196        /// 1197        /// # Arguments1198        /// 1199        /// * collection_id.1200        /// 1201        /// * schema: String representing the offchain data schema.1202        #[weight = T::WeightInfo::set_variable_meta_data()]1203        pub fn set_variable_meta_data (1204            origin,1205            collection_id: CollectionId,1206            item_id: TokenId,1207            data: Vec<u8>1208        ) -> DispatchResult {1209            let sender = ensure_signed(origin)?;1210            1211            Self::collection_exists(collection_id)?;1212            1213            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12141215            // Modify permissions check1216            let target_collection = <Collection<T>>::get(collection_id);1217            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1218                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1219                Error::<T>::NoPermission);12201221            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12221223            match target_collection.mode1224            {1225                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1226                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1227                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1228                _ => fail!(Error::<T>::UnexpectedCollectionType)1229            };12301231            Ok(())1232        }1233 1234        /// Set schema standard1235        /// ImageURL1236        /// Unique1237        /// 1238        /// # Permissions1239        /// 1240        /// * Collection Owner1241        /// * Collection Admin1242        /// 1243        /// # Arguments1244        /// 1245        /// * collection_id.1246        /// 1247        /// * schema: SchemaVersion: enum1248        #[weight = T::WeightInfo::set_schema_version()]1249        pub fn set_schema_version(1250            origin,1251            collection_id: CollectionId,1252            version: SchemaVersion1253        ) -> DispatchResult {1254            let sender = ensure_signed(origin)?;1255            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1256            let mut target_collection = <Collection<T>>::get(collection_id);1257            target_collection.schema_version = version;1258            <Collection<T>>::insert(collection_id, target_collection);12591260            Ok(())1261        }12621263        /// Set off-chain data schema.1264        /// 1265        /// # Permissions1266        /// 1267        /// * Collection Owner1268        /// * Collection Admin1269        /// 1270        /// # Arguments1271        /// 1272        /// * collection_id.1273        /// 1274        /// * schema: String representing the offchain data schema.1275        #[weight = T::WeightInfo::set_offchain_schema()]1276        pub fn set_offchain_schema(1277            origin,1278            collection_id: CollectionId,1279            schema: Vec<u8>1280        ) -> DispatchResult {1281            let sender = ensure_signed(origin)?;1282            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12831284            let mut target_collection = <Collection<T>>::get(collection_id);1285            target_collection.offchain_schema = schema;1286            <Collection<T>>::insert(collection_id, target_collection);12871288            Ok(())1289        }12901291        /// Set const on-chain data schema.1292        /// 1293        /// # Permissions1294        /// 1295        /// * Collection Owner1296        /// * Collection Admin1297        /// 1298        /// # Arguments1299        /// 1300        /// * collection_id.1301        /// 1302        /// * schema: String representing the const on-chain data schema.1303        #[weight = T::WeightInfo::set_const_on_chain_schema()]1304        pub fn set_const_on_chain_schema (1305            origin,1306            collection_id: CollectionId,1307            schema: Vec<u8>1308        ) -> DispatchResult {1309            let sender = ensure_signed(origin)?;1310            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13111312            let mut target_collection = <Collection<T>>::get(collection_id);1313            target_collection.const_on_chain_schema = schema;1314            <Collection<T>>::insert(collection_id, target_collection);13151316            Ok(())1317        }13181319        /// Set variable on-chain data schema.1320        /// 1321        /// # Permissions1322        /// 1323        /// * Collection Owner1324        /// * Collection Admin1325        /// 1326        /// # Arguments1327        /// 1328        /// * collection_id.1329        /// 1330        /// * schema: String representing the variable on-chain data schema.1331        #[weight = T::WeightInfo::set_const_on_chain_schema()]1332        pub fn set_variable_on_chain_schema (1333            origin,1334            collection_id: CollectionId,1335            schema: Vec<u8>1336        ) -> DispatchResult {1337            let sender = ensure_signed(origin)?;1338            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13391340            let mut target_collection = <Collection<T>>::get(collection_id);1341            target_collection.variable_on_chain_schema = schema;1342            <Collection<T>>::insert(collection_id, target_collection);13431344            Ok(())1345        }13461347        // Sudo permissions function1348        #[weight = T::WeightInfo::set_chain_limits()]1349        pub fn set_chain_limits(1350            origin,1351            limits: ChainLimits1352        ) -> DispatchResult {13531354            #[cfg(not(feature = "runtime-benchmarks"))]1355            ensure_root(origin)?;13561357            <ChainLimit>::put(limits);1358            Ok(())1359        }13601361        /// Enable smart contract self-sponsoring.1362        /// 1363        /// # Permissions1364        /// 1365        /// * Contract Owner1366        /// 1367        /// # Arguments1368        /// 1369        /// * contract address1370        /// * enable flag1371        /// 1372        #[weight = T::WeightInfo::enable_contract_sponsoring()]1373        pub fn enable_contract_sponsoring(1374            origin,1375            contract_address: T::AccountId,1376            enable: bool1377        ) -> DispatchResult {13781379            let sender = ensure_signed(origin)?;13801381            #[cfg(feature = "runtime-benchmarks")]1382            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13831384            Self::ensure_contract_owned(sender, &contract_address)?;13851386            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1387            Ok(())1388        }13891390        /// Set the rate limit for contract sponsoring to specified number of blocks.1391        /// 1392        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1393        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1394        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1395        /// from contract endowment if there are at least B blocks between such transactions. 1396        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1397        /// 1398        /// # Permissions1399        /// 1400        /// * Contract Owner1401        /// 1402        /// # Arguments1403        /// 1404        /// -`contract_address`: Address of the contract to sponsor1405        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1406        /// 1407        #[weight = T::WeightInfo::set_contract_sponsoring_rate_limit()]1408        pub fn set_contract_sponsoring_rate_limit(1409            origin,1410            contract_address: T::AccountId,1411            rate_limit: T::BlockNumber1412        ) -> DispatchResult {1413            let sender = ensure_signed(origin)?;14141415            #[cfg(feature = "runtime-benchmarks")]1416            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14171418            Self::ensure_contract_owned(sender, &contract_address)?;1419            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1420            Ok(())1421        }14221423        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1424        /// 1425        /// # Permissions1426        /// 1427        /// * Address that deployed smart contract.1428        /// 1429        /// # Arguments1430        /// 1431        /// -`contract_address`: Address of the contract.1432        /// 1433        /// - `enable`: .  1434        #[weight = T::WeightInfo::toggle_contract_white_list()]1435        pub fn toggle_contract_white_list(1436            origin,1437            contract_address: T::AccountId,1438            enable: bool1439        ) -> DispatchResult {1440            let sender = ensure_signed(origin)?;14411442            #[cfg(feature = "runtime-benchmarks")]1443            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14441445            Self::ensure_contract_owned(sender, &contract_address)?;1446            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1447            Ok(())1448        }1449        1450        /// Add an address to smart contract white list.1451        /// 1452        /// # Permissions1453        /// 1454        /// * Address that deployed smart contract.1455        /// 1456        /// # Arguments1457        /// 1458        /// -`contract_address`: Address of the contract.1459        ///1460        /// -`account_address`: Address to add.1461        #[weight = T::WeightInfo::add_to_contract_white_list()]1462        pub fn add_to_contract_white_list(1463            origin,1464            contract_address: T::AccountId,1465            account_address: T::AccountId1466        ) -> DispatchResult {1467            let sender = ensure_signed(origin)?;14681469            #[cfg(feature = "runtime-benchmarks")]1470            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1471            1472            Self::ensure_contract_owned(sender, &contract_address)?;      1473            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1474            Ok(())1475        }14761477        /// Remove an address from smart contract white list.1478        /// 1479        /// # Permissions1480        /// 1481        /// * Address that deployed smart contract.1482        /// 1483        /// # Arguments1484        /// 1485        /// -`contract_address`: Address of the contract.1486        ///1487        /// -`account_address`: Address to remove.1488        #[weight = T::WeightInfo::remove_from_contract_white_list()]1489        pub fn remove_from_contract_white_list(1490            origin,1491            contract_address: T::AccountId,1492            account_address: T::AccountId1493        ) -> DispatchResult {1494            let sender = ensure_signed(origin)?;14951496            #[cfg(feature = "runtime-benchmarks")]1497            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14981499            Self::ensure_contract_owned(sender, &contract_address)?;1500            <ContractWhiteList<T>>::remove(contract_address, account_address);1501            Ok(())1502        }15031504        #[weight = T::WeightInfo::set_collection_limits()]1505        pub fn set_collection_limits(1506            origin,1507            collection_id: u32,1508            limits: CollectionLimits,1509        ) -> DispatchResult {1510            let sender = ensure_signed(origin)?;1511            Self::check_owner_permissions(collection_id, sender.clone())?;1512            let mut target_collection = <Collection<T>>::get(collection_id);1513            let chain_limits = ChainLimit::get();1514            let climits = target_collection.limits;15151516            // collection bounds1517            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1518                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1519                Error::<T>::CollectionLimitBoundsExceeded);15201521            // token_limit   check  prev1522            ensure!(climits.token_limit > limits.token_limit && 1523                limits.token_limit <= chain_limits.account_token_ownership_limit, 1524                Error::<T>::AccountTokenLimitExceeded);15251526            target_collection.limits = limits;1527            <Collection<T>>::insert(collection_id, target_collection);15281529            Ok(())1530        } 1531    }1532}15331534impl<T: Trait> Module<T> {15351536    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15371538        // check token limit and account token limit1539        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1540        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1541        1542        Ok(())1543    }15441545    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15461547        // check token limit and account token limit1548        let total_items: u32 = ItemListIndex::get(collection_id);1549        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1550        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1551        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15521553        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1554            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1555            Self::check_white_list(collection_id, owner)?;1556            Self::check_white_list(collection_id, sender)?;1557        }15581559        Ok(())1560    }15611562    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1563        match target_collection.mode1564        {1565            CollectionMode::NFT => {1566                if let CreateItemData::NFT(data) = data {1567                    // check sizes1568                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1569                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1570                } else {1571                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1572                }1573            },1574            CollectionMode::Fungible(_) => {1575                if let CreateItemData::Fungible(_) = data {1576                } else {1577                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1578                }1579            },1580            CollectionMode::ReFungible(_) => {1581                if let CreateItemData::ReFungible(data) = data {15821583                    // check sizes1584                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1585                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1586                } else {1587                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1588                }1589            },1590            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1591        };15921593        Ok(())1594    }15951596    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1597        match data1598        {1599            CreateItemData::NFT(data) => {1600                let item = NftItemType {1601                    owner,1602                    const_data: data.const_data,1603                    variable_data: data.variable_data1604                };16051606                Self::add_nft_item(collection_id, item)?;1607            },1608            CreateItemData::Fungible(data) => {1609                Self::add_fungible_item(collection_id, &owner, data.value)?;1610            },1611            CreateItemData::ReFungible(data) => {1612                let mut owner_list = Vec::new();1613                let value = (10 as u128).pow(collection.decimal_points as u32);1614                owner_list.push(Ownership {owner: owner.clone(), fraction: value});16151616                let item = ReFungibleItemType {1617                    owner: owner_list,1618                    const_data: data.const_data,1619                    variable_data: data.variable_data1620                };16211622                Self::add_refungible_item(collection_id, item)?;1623            }1624        };16251626        // call event1627        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16281629        Ok(())1630    }16311632    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {16331634        // Does new owner already have an account?1635        let mut balance: u128 = 0;1636        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1637            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1638        } 16391640        // Mint 1641        let item = FungibleItemType {1642            value: balance + value1643        };1644        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16451646        // Update balance1647        let new_balance = <Balance<T>>::get(collection_id, owner)1648            .checked_add(value)1649            .ok_or(Error::<T>::NumOverflow)?;1650        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);16511652        Ok(())1653    }16541655    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1656        let current_index = <ItemListIndex>::get(collection_id)1657            .checked_add(1)1658            .ok_or(Error::<T>::NumOverflow)?;1659        let itemcopy = item.clone();16601661        let value = item.owner.first().unwrap().fraction;1662        let owner = item.owner.first().unwrap().owner.clone();16631664        Self::add_token_index(collection_id, current_index, owner.clone())?;16651666        <ItemListIndex>::insert(collection_id, current_index);1667        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);16681669        // Update balance1670        let new_balance = <Balance<T>>::get(collection_id, owner.clone())1671            .checked_add(value)1672            .ok_or(Error::<T>::NumOverflow)?;1673        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);16741675        Ok(())1676    }16771678    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1679        let current_index = <ItemListIndex>::get(collection_id)1680            .checked_add(1)1681            .ok_or(Error::<T>::NumOverflow)?;16821683        let item_owner = item.owner.clone();1684        Self::add_token_index(collection_id, current_index, item.owner.clone())?;16851686        <ItemListIndex>::insert(collection_id, current_index);1687        <NftItemList<T>>::insert(collection_id, current_index, item);16881689        // Update balance1690        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1691            .checked_add(1)1692            .ok_or(Error::<T>::NumOverflow)?;1693        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);16941695        Ok(())1696    }16971698    fn burn_refungible_item(1699        collection_id: CollectionId,1700        item_id: TokenId,1701        owner: T::AccountId,1702    ) -> DispatchResult {1703        ensure!(1704            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1705            Error::<T>::TokenNotFound1706        );1707        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1708        let item = collection1709            .owner1710            .iter()1711            .filter(|&i| i.owner == owner)1712            .next()1713            .unwrap();1714        Self::remove_token_index(collection_id, item_id, owner.clone())?;17151716        // update balance1717        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1718            .checked_sub(item.fraction)1719            .ok_or(Error::<T>::NumOverflow)?;1720        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17211722        <ReFungibleItemList<T>>::remove(collection_id, item_id);17231724        Ok(())1725    }17261727    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1728        ensure!(1729            <NftItemList<T>>::contains_key(collection_id, item_id),1730            Error::<T>::TokenNotFound1731        );1732        let item = <NftItemList<T>>::get(collection_id, item_id);1733        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17341735        // update balance1736        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1737            .checked_sub(1)1738            .ok_or(Error::<T>::NumOverflow)?;1739        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1740        <NftItemList<T>>::remove(collection_id, item_id);17411742        Ok(())1743    }17441745    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1746        ensure!(1747            <FungibleItemList<T>>::contains_key(collection_id, owner),1748            Error::<T>::TokenNotFound1749        );1750        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1751        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);17521753        // update balance1754        let new_balance = <Balance<T>>::get(collection_id, owner)1755            .checked_sub(value)1756            .ok_or(Error::<T>::NumOverflow)?;1757        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17581759        if balance.value - value > 0 {1760            balance.value -= value;1761            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1762        }1763        else {1764            <FungibleItemList<T>>::remove(collection_id, owner);1765        }17661767        Ok(())1768    }17691770    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1771        ensure!(1772            <Collection<T>>::contains_key(collection_id),1773            Error::<T>::CollectionNotFound1774        );1775        Ok(())1776    }17771778    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1779        Self::collection_exists(collection_id)?;17801781        let target_collection = <Collection<T>>::get(collection_id);1782        ensure!(1783            subject == target_collection.owner,1784            Error::<T>::NoPermission1785        );17861787        Ok(())1788    }17891790    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1791        let target_collection = <Collection<T>>::get(collection_id);1792        let mut result: bool = subject == target_collection.owner;1793        let exists = <AdminList<T>>::contains_key(collection_id);17941795        if !result & exists {1796            if <AdminList<T>>::get(collection_id).contains(&subject) {1797                result = true1798            }1799        }18001801        result1802    }18031804    fn check_owner_or_admin_permissions(1805        collection_id: CollectionId,1806        subject: T::AccountId,1807    ) -> DispatchResult {1808        Self::collection_exists(collection_id)?;1809        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18101811        ensure!(1812            result,1813            Error::<T>::NoPermission1814        );1815        Ok(())1816    }18171818    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1819        let target_collection = <Collection<T>>::get(collection_id);18201821        match target_collection.mode {1822            CollectionMode::NFT => {1823                <NftItemList<T>>::get(collection_id, item_id).owner == subject1824            }1825            CollectionMode::Fungible(_) => {1826                <FungibleItemList<T>>::contains_key(collection_id, &subject)1827            }1828            CollectionMode::ReFungible(_) => {1829                <ReFungibleItemList<T>>::get(collection_id, item_id)1830                    .owner1831                    .iter()1832                    .any(|i| i.owner == subject)1833            }1834            CollectionMode::Invalid => false,1835        }1836    }18371838    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1839        let mes = Error::<T>::AddresNotInWhiteList;1840        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);18411842        Ok(())1843    }18441845    fn transfer_fungible(1846        collection_id: CollectionId,1847        value: u128,1848        owner: &T::AccountId,1849        recipient: &T::AccountId,1850    ) -> DispatchResult {1851        ensure!(1852            <FungibleItemList<T>>::contains_key(collection_id, owner),1853            Error::<T>::TokenNotFound1854        );18551856        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1857        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);18581859        // Send balance to recipient (updates balanceOf of recipient)1860        Self::add_fungible_item(collection_id, recipient, value)?;18611862        // update balanceOf of sender1863        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);18641865        // Reduce or remove sender1866        if balance.value == value {1867            <FungibleItemList<T>>::remove(collection_id, owner);1868        }1869        else {1870            balance.value -= value;1871            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1872        }18731874        Ok(())1875    }18761877    fn transfer_refungible(1878        collection_id: CollectionId,1879        item_id: TokenId,1880        value: u128,1881        owner: T::AccountId,1882        new_owner: T::AccountId,1883    ) -> DispatchResult {1884        ensure!(1885            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1886            Error::<T>::TokenNotFound1887        );18881889        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1890        let item = full_item1891            .owner1892            .iter()1893            .filter(|i| i.owner == owner)1894            .next()1895            .ok_or(Error::<T>::NumOverflow)?;1896        let amount = item.fraction;18971898        ensure!(amount >= value, Error::<T>::TokenValueTooLow);18991900        // update balance1901        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1902            .checked_sub(value)1903            .ok_or(Error::<T>::NumOverflow)?;1904        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19051906        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1907            .checked_add(value)1908            .ok_or(Error::<T>::NumOverflow)?;1909        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19101911        let old_owner = item.owner.clone();1912        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19131914        // transfer1915        if amount == value && !new_owner_has_account {1916            // change owner1917            // new owner do not have account1918            let mut new_full_item = full_item.clone();1919            new_full_item1920                .owner1921                .iter_mut()1922                .find(|i| i.owner == owner)1923                .unwrap()1924                .owner = new_owner.clone();1925            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19261927            // update index collection1928            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1929        } else {1930            let mut new_full_item = full_item.clone();1931            new_full_item1932                .owner1933                .iter_mut()1934                .find(|i| i.owner == owner)1935                .unwrap()1936                .fraction -= value;19371938            // separate amount1939            if new_owner_has_account {1940                // new owner has account1941                new_full_item1942                    .owner1943                    .iter_mut()1944                    .find(|i| i.owner == new_owner)1945                    .unwrap()1946                    .fraction += value;1947            } else {1948                // new owner do not have account1949                new_full_item.owner.push(Ownership {1950                    owner: new_owner.clone(),1951                    fraction: value,1952                });1953                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1954            }19551956            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1957        }19581959        Ok(())1960    }19611962    fn transfer_nft(1963        collection_id: CollectionId,1964        item_id: TokenId,1965        sender: T::AccountId,1966        new_owner: T::AccountId,1967    ) -> DispatchResult {1968        ensure!(1969            <NftItemList<T>>::contains_key(collection_id, item_id),1970            Error::<T>::TokenNotFound1971        );19721973        let mut item = <NftItemList<T>>::get(collection_id, item_id);19741975        ensure!(1976            sender == item.owner,1977            Error::<T>::MustBeTokenOwner1978        );19791980        // update balance1981        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1982            .checked_sub(1)1983            .ok_or(Error::<T>::NumOverflow)?;1984        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19851986        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1987            .checked_add(1)1988            .ok_or(Error::<T>::NumOverflow)?;1989        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19901991        // change owner1992        let old_owner = item.owner.clone();1993        item.owner = new_owner.clone();1994        <NftItemList<T>>::insert(collection_id, item_id, item);19951996        // update index collection1997        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;19981999        Ok(())2000    }2001    2002    fn item_exists(2003        collection_id: CollectionId,2004        item_id: TokenId,2005        mode: &CollectionMode2006    ) -> DispatchResult {2007        match mode {2008            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2009            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2010            _ => ()2011        };2012        2013        Ok(())2014    }20152016    fn set_re_fungible_variable_data(2017        collection_id: CollectionId,2018        item_id: TokenId,2019        data: Vec<u8>2020    ) -> DispatchResult {2021        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20222023        item.variable_data = data;20242025        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20262027        Ok(())2028    }20292030    fn set_nft_variable_data(2031        collection_id: CollectionId,2032        item_id: TokenId,2033        data: Vec<u8>2034    ) -> DispatchResult {2035        let mut item = <NftItemList<T>>::get(collection_id, item_id);2036        2037        item.variable_data = data;20382039        <NftItemList<T>>::insert(collection_id, item_id, item);2040        2041        Ok(())2042    }20432044    fn init_collection(item: &CollectionType<T::AccountId>) {2045        // check params2046        assert!(2047            item.decimal_points <= MAX_DECIMAL_POINTS,2048            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2049        );2050        assert!(2051            item.name.len() <= 64,2052            "Collection name can not be longer than 63 char"2053        );2054        assert!(2055            item.name.len() <= 256,2056            "Collection description can not be longer than 255 char"2057        );2058        assert!(2059            item.token_prefix.len() <= 16,2060            "Token prefix can not be longer than 15 char"2061        );20622063        // Generate next collection ID2064        let next_id = CreatedCollectionCount::get()2065            .checked_add(1)2066            .unwrap();20672068        CreatedCollectionCount::put(next_id);2069    }20702071    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2072        let current_index = <ItemListIndex>::get(collection_id)2073            .checked_add(1)2074            .unwrap();20752076        let item_owner = item.owner.clone();2077        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();20782079        <ItemListIndex>::insert(collection_id, current_index);20802081        // Update balance2082        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2083            .checked_add(1)2084            .unwrap();2085        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2086    }20872088    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2089        let current_index = <ItemListIndex>::get(collection_id)2090            .checked_add(1)2091            .unwrap();20922093        Self::add_token_index(collection_id, current_index, (*owner).clone()).unwrap();20942095        <ItemListIndex>::insert(collection_id, current_index);20962097        // Update balance2098        let new_balance = <Balance<T>>::get(collection_id, owner)2099            .checked_add(item.value)2100            .unwrap();2101        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2102    }21032104    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2105        let current_index = <ItemListIndex>::get(collection_id)2106            .checked_add(1)2107            .unwrap();21082109        let value = item.owner.first().unwrap().fraction;2110        let owner = item.owner.first().unwrap().owner.clone();21112112        Self::add_token_index(collection_id, current_index, owner.clone()).unwrap();21132114        <ItemListIndex>::insert(collection_id, current_index);21152116        // Update balance2117        let new_balance = <Balance<T>>::get(collection_id, owner.clone())2118            .checked_add(value)2119            .unwrap();2120        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2121    }21222123    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {21242125        // add to account limit2126        if <AccountItemCount<T>>::contains_key(owner.clone()) {21272128            // bound Owned tokens by a single address2129            let count = <AccountItemCount<T>>::get(owner.clone());2130            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21312132            <AccountItemCount<T>>::insert(owner.clone(), count2133                .checked_add(1)2134                .ok_or(Error::<T>::NumOverflow)?);2135        }2136        else {2137            <AccountItemCount<T>>::insert(owner.clone(), 1);2138        }21392140        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2141        if list_exists {2142            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2143            let item_contains = list.contains(&item_index.clone());21442145            if !item_contains {2146                list.push(item_index.clone());2147            }21482149            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2150        } else {2151            let mut itm = Vec::new();2152            itm.push(item_index.clone());2153            <AddressTokens<T>>::insert(collection_id, owner, itm);2154            2155        }21562157        Ok(())2158    }21592160    fn remove_token_index(2161        collection_id: CollectionId,2162        item_index: TokenId,2163        owner: T::AccountId,2164    ) -> DispatchResult {21652166        // update counter2167        <AccountItemCount<T>>::insert(owner.clone(), 2168            <AccountItemCount<T>>::get(owner.clone())2169            .checked_sub(1)2170            .ok_or(Error::<T>::NumOverflow)?);217121722173        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2174        if list_exists {2175            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2176            let item_contains = list.contains(&item_index.clone());21772178            if item_contains {2179                list.retain(|&item| item != item_index);2180                <AddressTokens<T>>::insert(collection_id, owner, list);2181            }2182        }21832184        Ok(())2185    }21862187    fn move_token_index(2188        collection_id: CollectionId,2189        item_index: TokenId,2190        old_owner: T::AccountId,2191        new_owner: T::AccountId,2192    ) -> DispatchResult {2193        Self::remove_token_index(collection_id, item_index, old_owner)?;2194        Self::add_token_index(collection_id, item_index, new_owner)?;21952196        Ok(())2197    }2198    2199    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2200        if <ContractOwner<T>>::contains_key(contract.clone()) {2201            let owner = <ContractOwner<T>>::get(contract);2202            ensure!(account == owner, Error::<T>::NoPermission);2203        } else {2204            fail!(Error::<T>::NoPermission);2205        }22062207        Ok(())2208    }2209}22102211////////////////////////////////////////////////////////////////////////////////////////////////////2212// Economic models2213// #region22142215/// Fee multiplier.2216pub type Multiplier = FixedU128;22172218type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2219    <T as system::Trait>::AccountId,2220>>::Balance;2221type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2222    <T as system::Trait>::AccountId,2223>>::NegativeImbalance;22242225/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2226/// in the queue.2227#[derive(Encode, Decode, Clone, Eq, PartialEq)]2228pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2229    #[codec(compact)] BalanceOf<T>2230);22312232impl<T: Trait + Send + Sync> sp_std::fmt::Debug2233    for ChargeTransactionPayment<T>2234{2235    #[cfg(feature = "std")]2236    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2237        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2238    }2239    #[cfg(not(feature = "std"))]2240    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2241        Ok(())2242    }2243}22442245impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2246where2247    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2248    BalanceOf<T>: Send + Sync + FixedPointOperand,2249{2250    /// utility constructor. Used only in client/factory code.2251    pub fn from(fee: BalanceOf<T>) -> Self {2252        Self(fee)2253    }22542255    pub fn traditional_fee(2256        len: usize,2257        info: &DispatchInfoOf<T::Call>,2258        tip: BalanceOf<T>,2259    ) -> BalanceOf<T>2260    where2261        T::Call: Dispatchable<Info = DispatchInfo>,2262    {2263        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2264    }22652266	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2267		let weight_saturation = T::MaximumBlockWeight::get() / info.weight.max(1);2268		let len_saturation = T::MaximumBlockLength::get() as u64 / (len as u64).max(1);2269		let coefficient: BalanceOf<T> = weight_saturation.min(len_saturation).saturated_into::<BalanceOf<T>>();2270		final_fee.saturating_mul(coefficient).saturated_into::<TransactionPriority>()2271	}22722273    fn withdraw_fee(2274        &self,2275        who: &T::AccountId,2276        call: &T::Call,2277        info: &DispatchInfoOf<T::Call>,2278        len: usize,2279    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2280        let tip = self.0;22812282        // Set fee based on call type. Creating collection costs 1 Unique.2283        // All other transactions have traditional fees so far2284        // let fee = match call.is_sub_type() {2285        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2286        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2287        //                                                 // _ => <BalanceOf<T>>::from(100)2288        // };2289        let fee = Self::traditional_fee(len, info, tip);22902291        // Only mess with balances if fee is not zero.2292        if fee.is_zero() {2293            return Ok((fee, None));2294        }22952296        // Determine who is paying transaction fee based on ecnomic model2297        // Parse call to extract collection ID and access collection sponsor2298        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2299            Some(Call::create_item(collection_id, _owner, _properties)) => {23002301                // sponsor timeout2302                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;23032304                let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;2305                let mut sponsored = true;2306                if <CreateItemBasket<T>>::contains_key(collection_id) {2307                    let last_tx_block = <CreateItemBasket<T>>::get(collection_id);2308                    let limit_time = last_tx_block + limit.into();2309                    if block_number <= limit_time {2310                        sponsored = false;2311                    }2312                }2313                if sponsored {2314                    <CreateItemBasket<T>>::insert(collection_id, block_number);2315                }23162317                // check free create limit2318                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2319                   (<Collection<T>>::get(collection_id).sponsor_confirmed) &&2320                   (sponsored)2321                {2322                    <Collection<T>>::get(collection_id).sponsor2323                } else {2324                    T::AccountId::default()2325                }2326            }2327            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2328                2329                let mut sponsor_transfer = false;2330                if <Collection<T>>::get(collection_id).sponsor_confirmed {23312332                    let collection_limits = <Collection<T>>::get(collection_id).limits;2333                    let collection_mode = <Collection<T>>::get(collection_id).mode;2334    2335                    // sponsor timeout2336                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2337                    sponsor_transfer = match collection_mode {2338                        CollectionMode::NFT => {2339    2340                            // get correct limit2341                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2342                                collection_limits.sponsor_transfer_timeout2343                            } else {2344                                ChainLimit::get().nft_sponsor_transfer_timeout2345                            };2346    2347                            let mut sponsored = true;2348                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2349                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2350                                let limit_time = last_tx_block + limit.into();2351                                if block_number <= limit_time {2352                                    sponsored = false;2353                                }2354                            }2355                            if sponsored {2356                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2357                            }23582359                            sponsored2360                        }2361                        CollectionMode::Fungible(_) => {2362    2363                            // get correct limit2364                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2365                                collection_limits.sponsor_transfer_timeout2366                            } else {2367                                ChainLimit::get().fungible_sponsor_transfer_timeout2368                            };2369    2370                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2371                            let mut sponsored = true;2372                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2373                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2374                                let limit_time = last_tx_block + limit.into();2375                                if block_number <= limit_time {2376                                    sponsored = false;2377                                }2378                            }2379                            if sponsored {2380                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2381                            }23822383                            sponsored2384                        }2385                        CollectionMode::ReFungible(_) => {2386    2387                            // get correct limit2388                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2389                                collection_limits.sponsor_transfer_timeout2390                            } else {2391                                ChainLimit::get().refungible_sponsor_transfer_timeout2392                            };2393    2394                            let mut sponsored = true;2395                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2396                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2397                                let limit_time = last_tx_block + limit.into();2398                                if block_number <= limit_time {2399                                    sponsored = false;2400                                }2401                            }2402                            if sponsored {2403                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2404                            }24052406                            sponsored2407                        }2408                        _ => {2409                            false2410                        },2411                    };2412                }24132414                if !sponsor_transfer {2415                    T::AccountId::default()2416                } else {2417                    <Collection<T>>::get(collection_id).sponsor2418                }2419            }24202421            _ => T::AccountId::default(),2422        };24232424        // Sponsor smart contracts2425        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {24262427            // On instantiation: set the contract owner2428            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {24292430                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2431                    code_hash,2432                    &data,2433                    &who,2434                );2435                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());24362437                T::AccountId::default()2438            },24392440            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2441            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {24422443                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());24442445                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2446                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2447                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2448                  2449                if !owned_contract && white_list_enabled {2450                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2451                        return Err(InvalidTransaction::Call.into());2452                    }2453                }24542455                let mut sponsor_transfer = false;2456                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2457                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2458                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2459                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2460                    let limit_time = last_tx_block + rate_limit;24612462                    if block_number >= limit_time {2463                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2464                        sponsor_transfer = true;2465                    }2466                } else {2467                    sponsor_transfer = false;2468                }2469               2470                2471                let mut sp = T::AccountId::default();2472                if sponsor_transfer {2473                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2474                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2475                            sp = called_contract;2476                        }2477                    }2478                }24792480                sp2481            },24822483            _ => sponsor,2484        };24852486        let mut who_pays_fee: T::AccountId = sponsor.clone();2487        if sponsor == T::AccountId::default() {2488            who_pays_fee = who.clone();2489        }24902491        match <T as transaction_payment::Trait>::Currency::withdraw(2492            &who_pays_fee,2493            fee,2494            if tip.is_zero() {2495                WithdrawReason::TransactionPayment.into()2496            } else {2497                WithdrawReason::TransactionPayment | WithdrawReason::Tip2498            },2499            ExistenceRequirement::KeepAlive,2500        ) {2501            Ok(imbalance) => Ok((fee, Some(imbalance))),2502            Err(_) => Err(InvalidTransaction::Payment.into()),2503        }2504    }2505}250625072508impl<T: Trait + Send + Sync> SignedExtension2509    for ChargeTransactionPayment<T>2510where2511    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2512    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2513{2514    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2515    type AccountId = T::AccountId;2516    type Call = T::Call;2517    type AdditionalSigned = ();2518    type Pre = (2519        BalanceOf<T>,2520        Self::AccountId,2521        Option<NegativeImbalanceOf<T>>,2522        BalanceOf<T>,2523    );2524    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2525        Ok(())2526    }25272528    fn validate(2529        &self,2530        who: &Self::AccountId,2531        call: &Self::Call,2532        info: &DispatchInfoOf<Self::Call>,2533        len: usize,2534    ) -> TransactionValidity {2535		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2536		Ok(ValidTransaction {2537			priority: Self::get_priority(len, info, fee),2538			..Default::default()2539		})2540    }25412542    fn pre_dispatch(2543        self,2544        who: &Self::AccountId,2545        call: &Self::Call,2546        info: &DispatchInfoOf<Self::Call>,2547        len: usize,2548    ) -> Result<Self::Pre, TransactionValidityError> {2549        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2550        Ok((self.0, who.clone(), imbalance, fee))2551    }25522553    fn post_dispatch(2554        pre: Self::Pre,2555        info: &DispatchInfoOf<Self::Call>,2556        post_info: &PostDispatchInfoOf<Self::Call>,2557        len: usize,2558        _result: &DispatchResult,2559    ) -> Result<(), TransactionValidityError> {2560        let (tip, who, imbalance, fee) = pre;2561        if let Some(payed) = imbalance {2562            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2563                len as u32, info, post_info, tip,2564            );2565            let refund = fee.saturating_sub(actual_fee);2566            let actual_payment =2567                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2568                    &who, refund,2569                ) {2570                    Ok(refund_imbalance) => {2571                        // The refund cannot be larger than the up front payed max weight.2572                        // `PostDispatchInfo::calc_unspent` guards against such a case.2573                        match payed.offset(refund_imbalance) {2574                            Ok(actual_payment) => actual_payment,2575                            Err(_) => return Err(InvalidTransaction::Payment.into()),2576                        }2577                    }2578                    // We do not recreate the account using the refund. The up front payment2579                    // is gone in that case.2580                    Err(_) => payed,2581                };2582            let imbalances = actual_payment.split(tip);2583            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2584                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2585            );2586        }2587        Ok(())2588    }2589}25902591// #endregion