git.delta.rocks / unique-network / refs/commits / 44bf7a851a55

difftreelog

source

pallets/nft/src/lib.rs96.6 KiBsourcehistory
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, IsSubType,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial, DispatchClass,29    },30    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 sp_runtime::traits::StaticLookup;45use pallet_contracts::chain_extension::UncheckedFrom;46use transaction_payment::OnChargeTransaction;4748#[cfg(test)]49mod mock;5051#[cfg(test)]52mod tests;5354mod default_weights;5556pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;57pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;58pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;59pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6061// Structs62// #region6364pub type CollectionId = u32;65pub type TokenId = u32;66pub type DecimalPoints = u8;6768#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]69#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]70pub enum CollectionMode {71    Invalid,72    NFT,73    // decimal points74    Fungible(DecimalPoints),75    ReFungible,76}7778impl Default for CollectionMode {79    fn default() -> Self {80        Self::Invalid81    }82}8384impl Into<u8> for CollectionMode {85    fn into(self) -> u8 {86        match self {87            CollectionMode::Invalid => 0,88            CollectionMode::NFT => 1,89            CollectionMode::Fungible(_) => 2,90            CollectionMode::ReFungible => 3,91        }92    }93}9495#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]96#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]97pub enum AccessMode {98    Normal,99    WhiteList,100}101impl Default for AccessMode {102    fn default() -> Self {103        Self::Normal104    }105}106107#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]108#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]109pub enum SchemaVersion {110    ImageURL,111    Unique,112}113impl Default for SchemaVersion {114    fn default() -> Self {115        Self::ImageURL116    }117}118119#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]120#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]121pub struct Ownership<AccountId> {122    pub owner: AccountId,123    pub fraction: u128,124}125126#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub struct CollectionType<AccountId> {129    pub owner: AccountId,130    pub mode: CollectionMode,131    pub access: AccessMode,132    pub decimal_points: DecimalPoints,133    pub name: Vec<u16>,        // 64 include null escape char134    pub description: Vec<u16>, // 256 include null escape char135    pub token_prefix: Vec<u8>, // 16 include null escape char136    pub mint_mode: bool,137    pub offchain_schema: Vec<u8>,138    pub schema_version: SchemaVersion,139    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender140    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.141    pub limits: CollectionLimits, // Collection private restrictions 142    pub variable_on_chain_schema: Vec<u8>, //143    pub const_on_chain_schema: Vec<u8>, //144}145146#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]147#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]148pub struct NftItemType<AccountId> {149    pub owner: AccountId,150    pub const_data: Vec<u8>,151    pub variable_data: Vec<u8>,152}153154#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]155#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]156pub struct FungibleItemType {157    pub value: u128,158}159160#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]161#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]162pub struct ReFungibleItemType<AccountId> {163    pub owner: Vec<Ownership<AccountId>>,164    pub const_data: Vec<u8>,165    pub variable_data: Vec<u8>,166}167168// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]169// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170// pub struct VestingItem<AccountId, Moment> {171//     pub sender: AccountId,172//     pub recipient: AccountId,173//     pub collection_id: CollectionId,174//     pub item_id: TokenId,175//     pub amount: u64,176//     pub vesting_date: Moment,177// }178179#[derive(Encode, Decode, Debug, Clone, PartialEq)]180#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]181pub struct CollectionLimits {182    pub account_token_ownership_limit: u32,183    pub sponsored_data_size: u32,184    pub token_limit: u32,185186    // Timeouts for item types in passed blocks187    pub sponsor_transfer_timeout: u32,188    pub owner_can_transfer: bool,189    pub owner_can_destroy: bool,190}191192impl Default for CollectionLimits {193    fn default() -> CollectionLimits {194        CollectionLimits { 195            account_token_ownership_limit: 10_000_000, 196            token_limit: u32::max_value(),197            sponsored_data_size: u32::max_value(), 198            sponsor_transfer_timeout: 14400,199            owner_can_transfer: true,200            owner_can_destroy: true201        }202    }203}204205#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]206#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]207pub struct ChainLimits {208    pub collection_numbers_limit: u32,209    pub account_token_ownership_limit: u32,210    pub collections_admins_limit: u64,211    pub custom_data_limit: u32,212213    // Timeouts for item types in passed blocks214    pub nft_sponsor_transfer_timeout: u32,215    pub fungible_sponsor_transfer_timeout: u32,216    pub refungible_sponsor_transfer_timeout: u32,217218    // Schema limits219    pub offchain_schema_limit: u32,220    pub variable_on_chain_schema_limit: u32,221    pub const_on_chain_schema_limit: u32,222}223224pub trait WeightInfo {225	fn create_collection() -> Weight;226	fn destroy_collection() -> Weight;227	fn add_to_white_list() -> Weight;228	fn remove_from_white_list() -> Weight;229    fn set_public_access_mode() -> Weight;230    fn set_mint_permission() -> Weight;231    fn change_collection_owner() -> Weight;232    fn add_collection_admin() -> Weight;233    fn remove_collection_admin() -> Weight;234    fn set_collection_sponsor() -> Weight;235    fn confirm_sponsorship() -> Weight;236    fn remove_collection_sponsor() -> Weight;237    fn create_item(s: usize) -> Weight;238    fn burn_item() -> Weight;239    fn transfer() -> Weight;240    fn approve() -> Weight;241    fn transfer_from() -> Weight;242    fn set_offchain_schema() -> Weight;243    fn set_const_on_chain_schema() -> Weight;244    fn set_variable_on_chain_schema() -> Weight;245    fn set_variable_meta_data() -> Weight;246    fn enable_contract_sponsoring() -> Weight;247    fn set_schema_version() -> Weight;248    fn set_chain_limits() -> Weight;249    fn set_contract_sponsoring_rate_limit() -> Weight;250    fn toggle_contract_white_list() -> Weight;251    fn add_to_contract_white_list() -> Weight;252    fn remove_from_contract_white_list() -> Weight;253    fn set_collection_limits() -> Weight;254}255256#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]257#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]258pub struct CreateNftData {259    pub const_data: Vec<u8>,260    pub variable_data: Vec<u8>,261}262263#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]264#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]265pub struct CreateFungibleData {266    pub value: u128,267}268269#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]270#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]271pub struct CreateReFungibleData {272    pub const_data: Vec<u8>,273    pub variable_data: Vec<u8>,274    pub pieces: u128,275}276277#[derive(Encode, Decode, Debug, Clone, PartialEq)]278#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]279pub enum CreateItemData {280    NFT(CreateNftData),281    Fungible(CreateFungibleData),282    ReFungible(CreateReFungibleData),283}284285impl CreateItemData {286    pub fn len(&self) -> usize {287        let len = match self {288            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),289            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),290            _ => 0291        };292        293        return len;294    }295}296297impl From<CreateNftData> for CreateItemData {298    fn from(item: CreateNftData) -> Self {299        CreateItemData::NFT(item)300    }301}302303impl From<CreateReFungibleData> for CreateItemData {304    fn from(item: CreateReFungibleData) -> Self {305        CreateItemData::ReFungible(item)306    }307}308309impl From<CreateFungibleData> for CreateItemData {310    fn from(item: CreateFungibleData) -> Self {311        CreateItemData::Fungible(item)312    }313}314315316decl_error! {317	/// Error for non-fungible-token module.318	pub enum Error for Module<T: Config> {319        /// Total collections bound exceeded.320        TotalCollectionsLimitExceeded,321		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.322        CollectionDecimalPointLimitExceeded, 323        /// Collection name can not be longer than 63 char.324        CollectionNameLimitExceeded, 325        /// Collection description can not be longer than 255 char.326        CollectionDescriptionLimitExceeded, 327        /// Token prefix can not be longer than 15 char.328        CollectionTokenPrefixLimitExceeded,329        /// This collection does not exist.330        CollectionNotFound,331        /// Item not exists.332        TokenNotFound,333        /// Admin not found334        AdminNotFound,335        /// Arithmetic calculation overflow.336        NumOverflow,       337        /// Account already has admin role.338        AlreadyAdmin,  339        /// You do not own this collection.340        NoPermission,341        /// This address is not set as sponsor, use setCollectionSponsor first.342        ConfirmUnsetSponsorFail,343        /// Collection is not in mint mode.344        PublicMintingNotAllowed,345        /// Sender parameter and item owner must be equal.346        MustBeTokenOwner,347        /// Item balance not enough.348        TokenValueTooLow,349        /// Size of item is too large.350        NftSizeLimitExceeded,351        /// No approve found352        ApproveNotFound,353        /// Requested value more than approved.354        TokenValueNotEnough,355        /// Only approved addresses can call this method.356        ApproveRequired,357        /// Address is not in white list.358        AddresNotInWhiteList,359        /// Number of collection admins bound exceeded.360        CollectionAdminsLimitExceeded,361        /// Owned tokens by a single address bound exceeded.362        AddressOwnershipLimitExceeded,363        /// Length of items properties must be greater than 0.364        EmptyArgument,365        /// const_data exceeded data limit.366        TokenConstDataLimitExceeded,367        /// variable_data exceeded data limit.368        TokenVariableDataLimitExceeded,369        /// Not NFT item data used to mint in NFT collection.370        NotNftDataUsedToMintNftCollectionToken,371        /// Not Fungible item data used to mint in Fungible collection.372        NotFungibleDataUsedToMintFungibleCollectionToken,373        /// Not Re Fungible item data used to mint in Re Fungible collection.374        NotReFungibleDataUsedToMintReFungibleCollectionToken,375        /// Unexpected collection type.376        UnexpectedCollectionType,377        /// Can't store metadata in fungible tokens.378        CantStoreMetadataInFungibleTokens,379        /// Collection token limit exceeded380        CollectionTokenLimitExceeded,381        /// Account token limit exceeded per collection382        AccountTokenLimitExceeded,383        /// Collection limit bounds per collection exceeded384        CollectionLimitBoundsExceeded,385        /// Schema data size limit bound exceeded386        SchemaDataLimitExceeded,387        /// Maximum refungibility exceeded388        WrongRefungiblePieces389	}390}391392pub trait Config: system::Config + Sized + transaction_payment::Config + pallet_contracts::Config {393    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;394395    /// Weight information for extrinsics in this pallet.396	type WeightInfo: WeightInfo;397}398399#[cfg(feature = "runtime-benchmarks")]400mod benchmarking;401402// #endregion403404decl_storage! {405    trait Store for Module<T: Config> as Nft {406407        // Private members408        NextCollectionID: CollectionId;409        CreatedCollectionCount: u32;410        ChainVersion: u64;411        ItemListIndex: map hasher(identity) CollectionId => TokenId;412413        // Chain limits struct414        pub ChainLimit get(fn chain_limit) config(): ChainLimits;415416        // Bound counters417        CollectionCount: u32;418        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;419420        // Basic collections421        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;422        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;423        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;424425        /// Balance owner per collection map426        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;427428        /// second parameter: item id + owner account id + spender account id429        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;430431        /// Item collections432        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;433        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;434        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;435436        /// Index list437        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;438439        /// Tokens transfer baskets440        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;441        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;442        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;443        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;444445        // Contract Sponsorship and Ownership446        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;447        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;448        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;449        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;450        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 451        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 452    }453    add_extra_genesis {454        build(|config: &GenesisConfig<T>| {455            // Modification of storage456            for (_num, _c) in &config.collection {457                <Module<T>>::init_collection(_c);458            }459460            for (_num, _c, _i) in &config.nft_item_id {461                <Module<T>>::init_nft_token(*_c, _i);462            }463464            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {465                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);466            }467468            for (_num, _c, _i) in &config.refungible_item_id {469                <Module<T>>::init_refungible_token(*_c, _i);470            }471        })472    }473}474475decl_event!(476    pub enum Event<T>477    where478        AccountId = <T as system::Config>::AccountId,479    {480        /// New collection was created481        /// 482        /// # Arguments483        /// 484        /// * collection_id: Globally unique identifier of newly created collection.485        /// 486        /// * mode: [CollectionMode] converted into u8.487        /// 488        /// * account_id: Collection owner.489        Created(CollectionId, u8, AccountId),490491        /// New item was created.492        /// 493        /// # Arguments494        /// 495        /// * collection_id: Id of the collection where item was created.496        /// 497        /// * item_id: Id of an item. Unique within the collection.498        ItemCreated(CollectionId, TokenId),499500        /// Collection item was burned.501        /// 502        /// # Arguments503        /// 504        /// collection_id.505        /// 506        /// item_id: Identifier of burned NFT.507        ItemDestroyed(CollectionId, TokenId),508509        /// Item was transferred510        ///511        /// * collection_id: Id of collection to which item is belong512        ///513        /// * item_id: Id of an item514        ///515        /// * sender: Original owner of item516        ///517        /// * recipient: New owner of item518        ///519        /// * amount: Always 1 for NFT520        Transfer(CollectionId, TokenId, AccountId, AccountId, u128),521    }522);523524decl_module! {525    pub struct Module<T: Config> for enum Call 526    where 527        origin: T::Origin528    {529        fn deposit_event() = default;530        type Error = Error<T>;531532        fn on_initialize(now: T::BlockNumber) -> Weight {533534            if ChainVersion::get() < 2535            {536                let value = NextCollectionID::get();537                CreatedCollectionCount::put(value);538                ChainVersion::put(2);539            }540541            0542        }543544        /// 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.545        /// 546        /// # Permissions547        /// 548        /// * Anyone.549        /// 550        /// # Arguments551        /// 552        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.553        /// 554        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.555        /// 556        /// * token_prefix: UTF-8 string with token prefix.557        /// 558        /// * mode: [CollectionMode] collection type and type dependent data.559        // returns collection ID560        #[weight = <T as Config>::WeightInfo::create_collection()]561        pub fn create_collection(origin,562                                 collection_name: Vec<u16>,563                                 collection_description: Vec<u16>,564                                 token_prefix: Vec<u8>,565                                 mode: CollectionMode) -> DispatchResult {566567            // Anyone can create a collection568            let who = ensure_signed(origin)?;569570            let decimal_points = match mode {571                CollectionMode::Fungible(points) => points,572                _ => 0573            };574575            // bound Total number of collections576            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);577578            // check params579            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);580            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);581            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);582            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);583584            // Generate next collection ID585            let next_id = CreatedCollectionCount::get()586                .checked_add(1)587                .ok_or(Error::<T>::NumOverflow)?;588589            // bound counter590            let total = CollectionCount::get()591                .checked_add(1)592                .ok_or(Error::<T>::NumOverflow)?;593594            CreatedCollectionCount::put(next_id);595            CollectionCount::put(total);596597            // Create new collection598            let new_collection = CollectionType {599                owner: who.clone(),600                name: collection_name,601                mode: mode.clone(),602                mint_mode: false,603                access: AccessMode::Normal,604                description: collection_description,605                decimal_points: decimal_points,606                token_prefix: token_prefix,607                offchain_schema: Vec::new(),608                schema_version: SchemaVersion::ImageURL,609                sponsor: T::AccountId::default(),610                sponsor_confirmed: false,611                variable_on_chain_schema: Vec::new(),612                const_on_chain_schema: Vec::new(),613                limits: CollectionLimits::default()614            };615616            // Add new collection to map617            <Collection<T>>::insert(next_id, new_collection);618619            // call event620            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));621622            Ok(())623        }624625        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.626        /// 627        /// # Permissions628        /// 629        /// * Collection Owner.630        /// 631        /// # Arguments632        /// 633        /// * collection_id: collection to destroy.634        #[weight = <T as Config>::WeightInfo::destroy_collection()]635        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {636637            let sender = ensure_signed(origin)?;638            Self::check_owner_permissions(collection_id, sender)?;639640            <AddressTokens<T>>::remove_prefix(collection_id);641            <Allowances<T>>::remove_prefix(collection_id);642            <Balance<T>>::remove_prefix(collection_id);643            <ItemListIndex>::remove(collection_id);644            <AdminList<T>>::remove(collection_id);645            <Collection<T>>::remove(collection_id);646            <WhiteList<T>>::remove_prefix(collection_id);647648            <NftItemList<T>>::remove_prefix(collection_id);649            <FungibleItemList<T>>::remove_prefix(collection_id);650            <ReFungibleItemList<T>>::remove_prefix(collection_id);651652            <NftTransferBasket<T>>::remove_prefix(collection_id);653            <FungibleTransferBasket<T>>::remove_prefix(collection_id);654            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);655656            if CollectionCount::get() > 0657            {658                // bound couter659                let total = CollectionCount::get()660                    .checked_sub(1)661                    .ok_or(Error::<T>::NumOverflow)?;662663                CollectionCount::put(total);664            }665666            Ok(())667        }668669        /// Add an address to white list.670        /// 671        /// # Permissions672        /// 673        /// * Collection Owner674        /// * Collection Admin675        /// 676        /// # Arguments677        /// 678        /// * collection_id.679        /// 680        /// * address.681        #[weight = <T as Config>::WeightInfo::add_to_white_list()]682        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{683684            let sender = ensure_signed(origin)?;685            Self::check_owner_or_admin_permissions(collection_id, sender)?;686687            <WhiteList<T>>::insert(collection_id, address, true);688            689            Ok(())690        }691692        /// Remove an address from white list.693        /// 694        /// # Permissions695        /// 696        /// * Collection Owner697        /// * Collection Admin698        /// 699        /// # Arguments700        /// 701        /// * collection_id.702        /// 703        /// * address.704        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]705        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{706707            let sender = ensure_signed(origin)?;708            Self::check_owner_or_admin_permissions(collection_id, sender)?;709710            <WhiteList<T>>::remove(collection_id, address);711712            Ok(())713        }714715        /// Toggle between normal and white list access for the methods with access for `Anyone`.716        /// 717        /// # Permissions718        /// 719        /// * Collection Owner.720        /// 721        /// # Arguments722        /// 723        /// * collection_id.724        /// 725        /// * mode: [AccessMode]726        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]727        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult728        {729            let sender = ensure_signed(origin)?;730731            Self::check_owner_permissions(collection_id, sender)?;732            let mut target_collection = <Collection<T>>::get(collection_id);733            target_collection.access = mode;734            <Collection<T>>::insert(collection_id, target_collection);735736            Ok(())737        }738739        /// Allows Anyone to create tokens if:740        /// * White List is enabled, and741        /// * Address is added to white list, and742        /// * This method was called with True parameter743        /// 744        /// # Permissions745        /// * Collection Owner746        ///747        /// # Arguments748        /// 749        /// * collection_id.750        /// 751        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.752        #[weight = <T as Config>::WeightInfo::set_mint_permission()]753        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult754        {755            let sender = ensure_signed(origin)?;756757            Self::check_owner_permissions(collection_id, sender)?;758            let mut target_collection = <Collection<T>>::get(collection_id);759            target_collection.mint_mode = mint_permission;760            <Collection<T>>::insert(collection_id, target_collection);761762            Ok(())763        }764765        /// Change the owner of the collection.766        /// 767        /// # Permissions768        /// 769        /// * Collection Owner.770        /// 771        /// # Arguments772        /// 773        /// * collection_id.774        /// 775        /// * new_owner.776        #[weight = <T as Config>::WeightInfo::change_collection_owner()]777        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {778779            let sender = ensure_signed(origin)?;780            Self::check_owner_permissions(collection_id, sender)?;781            let mut target_collection = <Collection<T>>::get(collection_id);782            target_collection.owner = new_owner;783            <Collection<T>>::insert(collection_id, target_collection);784785            Ok(())786        }787788        /// Adds an admin of the Collection.789        /// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership. 790        /// 791        /// # Permissions792        /// 793        /// * Collection Owner.794        /// * Collection Admin.795        /// 796        /// # Arguments797        /// 798        /// * collection_id: ID of the Collection to add admin for.799        /// 800        /// * new_admin_id: Address of new admin to add.801        #[weight = <T as Config>::WeightInfo::add_collection_admin()]802        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {803804            let sender = ensure_signed(origin)?;805            Self::check_owner_or_admin_permissions(collection_id, sender)?;806            let mut admin_arr: Vec<T::AccountId> = Vec::new();807808            if <AdminList<T>>::contains_key(collection_id)809            {810                admin_arr = <AdminList<T>>::get(collection_id);811                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);812            }813814            // Number of collection admins815            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);816817            admin_arr.push(new_admin_id);818            <AdminList<T>>::insert(collection_id, admin_arr);819820            Ok(())821        }822823        /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.824        ///825        /// # Permissions826        /// 827        /// * Collection Owner.828        /// * Collection Admin.829        /// 830        /// # Arguments831        /// 832        /// * collection_id: ID of the Collection to remove admin for.833        /// 834        /// * account_id: Address of admin to remove.835        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]836        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {837838            let sender = ensure_signed(origin)?;839            Self::check_owner_or_admin_permissions(collection_id, sender)?;840            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);841842            let mut admin_arr = <AdminList<T>>::get(collection_id);843            admin_arr.retain(|i| *i != account_id);844            <AdminList<T>>::insert(collection_id, admin_arr);845846            Ok(())847        }848849        /// # Permissions850        /// 851        /// * Collection Owner852        /// 853        /// # Arguments854        /// 855        /// * collection_id.856        /// 857        /// * new_sponsor.858        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]859        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {860861            let sender = ensure_signed(origin)?;862            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);863864            let mut target_collection = <Collection<T>>::get(collection_id);865            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);866867            target_collection.sponsor = new_sponsor;868            target_collection.sponsor_confirmed = false;869            <Collection<T>>::insert(collection_id, target_collection);870871            Ok(())872        }873874        /// # Permissions875        /// 876        /// * Sponsor.877        /// 878        /// # Arguments879        /// 880        /// * collection_id.881        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]882        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {883884            let sender = ensure_signed(origin)?;885            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);886887            let mut target_collection = <Collection<T>>::get(collection_id);888            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);889890            target_collection.sponsor_confirmed = true;891            <Collection<T>>::insert(collection_id, target_collection);892893            Ok(())894        }895896        /// Switch back to pay-per-own-transaction model.897        ///898        /// # Permissions899        ///900        /// * Collection owner.901        /// 902        /// # Arguments903        /// 904        /// * collection_id.905        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]906        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {907908            let sender = ensure_signed(origin)?;909            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);910911            let mut target_collection = <Collection<T>>::get(collection_id);912            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);913914            target_collection.sponsor = T::AccountId::default();915            target_collection.sponsor_confirmed = false;916            <Collection<T>>::insert(collection_id, target_collection);917918            Ok(())919        }920921        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.922        /// 923        /// # Permissions924        /// 925        /// * Collection Owner.926        /// * Collection Admin.927        /// * Anyone if928        ///     * White List is enabled, and929        ///     * Address is added to white list, and930        ///     * MintPermission is enabled (see SetMintPermission method)931        /// 932        /// # Arguments933        /// 934        /// * collection_id: ID of the collection.935        /// 936        /// * owner: Address, initial owner of the NFT.937        ///938        /// * data: Token data to store on chain.939        // #[weight =940        // (130_000_000 as Weight)941        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))942        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))943        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]944945        #[weight = <T as Config>::WeightInfo::create_item(data.len())]946        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {947948            let sender = ensure_signed(origin)?;949950            Self::collection_exists(collection_id)?;951952            let target_collection = <Collection<T>>::get(collection_id);953954            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;955            Self::validate_create_item_args(&target_collection, &data)?;956            Self::create_item_no_validation(collection_id, owner, data)?;957958            Ok(())959        }960961        /// This method creates multiple instances of NFT Collection created with CreateCollection method.962        /// 963        /// # Permissions964        /// 965        /// * Collection Owner.966        /// * Collection Admin.967        /// * Anyone if968        ///     * White List is enabled, and969        ///     * Address is added to white list, and970        ///     * MintPermission is enabled (see SetMintPermission method)971        /// 972        /// # Arguments973        /// 974        /// * collection_id: ID of the collection.975        /// 976        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].977        /// 978        /// * owner: Address, initial owner of the NFT.979        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()980                               .map(|data| { data.len() })981                               .sum())]982        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {983984            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);985            let sender = ensure_signed(origin)?;986987            Self::collection_exists(collection_id)?;988            let target_collection = <Collection<T>>::get(collection_id);989990            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;991992            for data in &items_data {993                Self::validate_create_item_args(&target_collection, data)?;994            }995            for data in &items_data {996                Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?;997            }998999            Ok(())1000        }10011002        /// Destroys a concrete instance of NFT.1003        /// 1004        /// # Permissions1005        /// 1006        /// * Collection Owner.1007        /// * Collection Admin.1008        /// * Current NFT Owner.1009        /// 1010        /// # Arguments1011        /// 1012        /// * collection_id: ID of the collection.1013        /// 1014        /// * item_id: ID of NFT to burn.1015        #[weight = <T as Config>::WeightInfo::burn_item()]1016        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10171018            let sender = ensure_signed(origin)?;1019            Self::collection_exists(collection_id)?;10201021            // Transfer permissions check1022            let target_collection = <Collection<T>>::get(collection_id);1023            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1024                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1025                Error::<T>::NoPermission);10261027            if target_collection.access == AccessMode::WhiteList {1028                Self::check_white_list(collection_id, &sender)?;1029            }10301031            match target_collection.mode1032            {1033                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1034                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1035                CollectionMode::ReFungible  => Self::burn_refungible_item(collection_id, item_id, &sender)?,1036                _ => ()1037            };10381039            // call event1040            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10411042            Ok(())1043        }10441045        /// Change ownership of the token.1046        /// 1047        /// # Permissions1048        /// 1049        /// * Collection Owner1050        /// * Collection Admin1051        /// * Current NFT owner1052        ///1053        /// # Arguments1054        /// 1055        /// * recipient: Address of token recipient.1056        /// 1057        /// * collection_id.1058        /// 1059        /// * item_id: ID of the item1060        ///     * Non-Fungible Mode: Required.1061        ///     * Fungible Mode: Ignored.1062        ///     * Re-Fungible Mode: Required.1063        /// 1064        /// * value: Amount to transfer.1065        ///     * Non-Fungible Mode: Ignored1066        ///     * Fungible Mode: Must specify transferred amount1067        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1068        #[weight = <T as Config>::WeightInfo::transfer()]1069        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1070            let sender = ensure_signed(origin)?;1071            Self::transfer_internal(sender, recipient, collection_id, item_id, value)1072        }10731074        /// Set, change, or remove approved address to transfer the ownership of the NFT.1075        /// 1076        /// # Permissions1077        /// 1078        /// * Collection Owner1079        /// * Collection Admin1080        /// * Current NFT owner1081        /// 1082        /// # Arguments1083        /// 1084        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1085        /// 1086        /// * collection_id.1087        /// 1088        /// * item_id: ID of the item.1089        #[weight = <T as Config>::WeightInfo::approve()]1090        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {10911092            let sender = ensure_signed(origin)?;10931094            Self::collection_exists(collection_id)?;1095            Self::token_exists(collection_id, item_id, &sender)?;10961097            // Transfer permissions check1098            let target_collection = <Collection<T>>::get(collection_id);1099            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1100                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1101                Error::<T>::NoPermission);11021103            if target_collection.access == AccessMode::WhiteList {1104                Self::check_white_list(collection_id, &sender)?;1105                Self::check_white_list(collection_id, &spender)?;1106            }11071108            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1109            let mut allowance: u128 = amount;1110            if allowance_exists {1111                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1112            }1113            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11141115            Ok(())1116        }1117        1118        /// 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.1119        /// 1120        /// # Permissions1121        /// * Collection Owner1122        /// * Collection Admin1123        /// * Current NFT owner1124        /// * Address approved by current NFT owner1125        /// 1126        /// # Arguments1127        /// 1128        /// * from: Address that owns token.1129        /// 1130        /// * recipient: Address of token recipient.1131        /// 1132        /// * collection_id.1133        /// 1134        /// * item_id: ID of the item.1135        /// 1136        /// * value: Amount to transfer.1137        #[weight = <T as Config>::WeightInfo::transfer_from()]1138        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11391140            let sender = ensure_signed(origin)?;1141            let mut appoved_transfer = false;11421143            // Check approval1144            let mut approval: u128 = 0;1145            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1146                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1147                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1148                appoved_transfer = true;1149            }11501151            let target_collection = <Collection<T>>::get(collection_id);11521153            // Limits check1154            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11551156            // Transfer permissions check         1157            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1158                Error::<T>::NoPermission);11591160            if target_collection.access == AccessMode::WhiteList {1161                Self::check_white_list(collection_id, &sender)?;1162                Self::check_white_list(collection_id, &recipient)?;1163            }11641165            // Reduce approval by transferred amount or remove if remaining approval drops to 01166            if approval.checked_sub(value).unwrap_or(0) > 0 {1167                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1168            }1169            else {1170                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1171            }11721173            match target_collection.mode1174            {1175                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1176                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1177                CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1178                _ => ()1179            };11801181            Ok(())1182        }11831184        // #[weight = 0]1185        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11861187        //     // let no_perm_mes = "You do not have permissions to modify this collection";1188        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1189        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1190        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11911192        //     // // on_nft_received  call11931194        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;11951196        //     Ok(())1197        // }11981199        /// Set off-chain data schema.1200        /// 1201        /// # Permissions1202        /// 1203        /// * Collection Owner1204        /// * Collection Admin1205        /// 1206        /// # Arguments1207        /// 1208        /// * collection_id.1209        /// 1210        /// * schema: String representing the offchain data schema.1211        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1212        pub fn set_variable_meta_data (1213            origin,1214            collection_id: CollectionId,1215            item_id: TokenId,1216            data: Vec<u8>1217        ) -> DispatchResult {1218            let sender = ensure_signed(origin)?;1219            1220            Self::collection_exists(collection_id)?;1221            Self::token_exists(collection_id, item_id, &sender)?;12221223            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12241225            // Modify permissions check1226            let target_collection = <Collection<T>>::get(collection_id);1227            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1228                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1229                Error::<T>::NoPermission);12301231            match target_collection.mode1232            {1233                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1234                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1235                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1236                _ => fail!(Error::<T>::UnexpectedCollectionType)1237            };12381239            Ok(())1240        }1241 1242        /// Set schema standard1243        /// ImageURL1244        /// Unique1245        /// 1246        /// # Permissions1247        /// 1248        /// * Collection Owner1249        /// * Collection Admin1250        /// 1251        /// # Arguments1252        /// 1253        /// * collection_id.1254        /// 1255        /// * schema: SchemaVersion: enum1256        #[weight = <T as Config>::WeightInfo::set_schema_version()]1257        pub fn set_schema_version(1258            origin,1259            collection_id: CollectionId,1260            version: SchemaVersion1261        ) -> DispatchResult {1262            let sender = ensure_signed(origin)?;1263            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1264            let mut target_collection = <Collection<T>>::get(collection_id);1265            target_collection.schema_version = version;1266            <Collection<T>>::insert(collection_id, target_collection);12671268            Ok(())1269        }12701271        /// Set off-chain data schema.1272        /// 1273        /// # Permissions1274        /// 1275        /// * Collection Owner1276        /// * Collection Admin1277        /// 1278        /// # Arguments1279        /// 1280        /// * collection_id.1281        /// 1282        /// * schema: String representing the offchain data schema.1283        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1284        pub fn set_offchain_schema(1285            origin,1286            collection_id: CollectionId,1287            schema: Vec<u8>1288        ) -> DispatchResult {1289            let sender = ensure_signed(origin)?;1290            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12911292            // check schema limit1293            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");12941295            let mut target_collection = <Collection<T>>::get(collection_id);1296            target_collection.offchain_schema = schema;1297            <Collection<T>>::insert(collection_id, target_collection);12981299            Ok(())1300        }13011302        /// Set const on-chain data schema.1303        /// 1304        /// # Permissions1305        /// 1306        /// * Collection Owner1307        /// * Collection Admin1308        /// 1309        /// # Arguments1310        /// 1311        /// * collection_id.1312        /// 1313        /// * schema: String representing the const on-chain data schema.1314        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1315        pub fn set_const_on_chain_schema (1316            origin,1317            collection_id: CollectionId,1318            schema: Vec<u8>1319        ) -> DispatchResult {1320            let sender = ensure_signed(origin)?;1321            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13221323            // check schema limit1324            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");13251326            let mut target_collection = <Collection<T>>::get(collection_id);1327            target_collection.const_on_chain_schema = schema;1328            <Collection<T>>::insert(collection_id, target_collection);13291330            Ok(())1331        }13321333        /// Set variable on-chain data schema.1334        /// 1335        /// # Permissions1336        /// 1337        /// * Collection Owner1338        /// * Collection Admin1339        /// 1340        /// # Arguments1341        /// 1342        /// * collection_id.1343        /// 1344        /// * schema: String representing the variable on-chain data schema.1345        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1346        pub fn set_variable_on_chain_schema (1347            origin,1348            collection_id: CollectionId,1349            schema: Vec<u8>1350        ) -> DispatchResult {1351            let sender = ensure_signed(origin)?;1352            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13531354            // check schema limit1355            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");13561357            let mut target_collection = <Collection<T>>::get(collection_id);1358            target_collection.variable_on_chain_schema = schema;1359            <Collection<T>>::insert(collection_id, target_collection);13601361            Ok(())1362        }13631364        // Sudo permissions function1365        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1366        pub fn set_chain_limits(1367            origin,1368            limits: ChainLimits1369        ) -> DispatchResult {13701371            #[cfg(not(feature = "runtime-benchmarks"))]1372            ensure_root(origin)?;13731374            <ChainLimit>::put(limits);1375            Ok(())1376        }13771378        /// Enable smart contract self-sponsoring.1379        /// 1380        /// # Permissions1381        /// 1382        /// * Contract Owner1383        /// 1384        /// # Arguments1385        /// 1386        /// * contract address1387        /// * enable flag1388        /// 1389        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1390        pub fn enable_contract_sponsoring(1391            origin,1392            contract_address: T::AccountId,1393            enable: bool1394        ) -> DispatchResult {13951396            let sender = ensure_signed(origin)?;13971398            #[cfg(feature = "runtime-benchmarks")]1399            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14001401            Self::ensure_contract_owned(sender, &contract_address)?;14021403            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1404            Ok(())1405        }14061407        /// Set the rate limit for contract sponsoring to specified number of blocks.1408        /// 1409        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1410        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1411        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1412        /// from contract endowment if there are at least B blocks between such transactions. 1413        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1414        /// 1415        /// # Permissions1416        /// 1417        /// * Contract Owner1418        /// 1419        /// # Arguments1420        /// 1421        /// -`contract_address`: Address of the contract to sponsor1422        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1423        /// 1424        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1425        pub fn set_contract_sponsoring_rate_limit(1426            origin,1427            contract_address: T::AccountId,1428            rate_limit: T::BlockNumber1429        ) -> DispatchResult {1430            let sender = ensure_signed(origin)?;14311432            #[cfg(feature = "runtime-benchmarks")]1433            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14341435            Self::ensure_contract_owned(sender, &contract_address)?;1436            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1437            Ok(())1438        }14391440        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1441        /// 1442        /// # Permissions1443        /// 1444        /// * Address that deployed smart contract.1445        /// 1446        /// # Arguments1447        /// 1448        /// -`contract_address`: Address of the contract.1449        /// 1450        /// - `enable`: .  1451        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1452        pub fn toggle_contract_white_list(1453            origin,1454            contract_address: T::AccountId,1455            enable: bool1456        ) -> DispatchResult {1457            let sender = ensure_signed(origin)?;14581459            #[cfg(feature = "runtime-benchmarks")]1460            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14611462            Self::ensure_contract_owned(sender, &contract_address)?;1463            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1464            Ok(())1465        }1466        1467        /// Add an address to smart contract white list.1468        /// 1469        /// # Permissions1470        /// 1471        /// * Address that deployed smart contract.1472        /// 1473        /// # Arguments1474        /// 1475        /// -`contract_address`: Address of the contract.1476        ///1477        /// -`account_address`: Address to add.1478        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1479        pub fn add_to_contract_white_list(1480            origin,1481            contract_address: T::AccountId,1482            account_address: T::AccountId1483        ) -> DispatchResult {1484            let sender = ensure_signed(origin)?;14851486            #[cfg(feature = "runtime-benchmarks")]1487            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1488            1489            Self::ensure_contract_owned(sender, &contract_address)?;      1490            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1491            Ok(())1492        }14931494        /// Remove an address from smart contract white list.1495        /// 1496        /// # Permissions1497        /// 1498        /// * Address that deployed smart contract.1499        /// 1500        /// # Arguments1501        /// 1502        /// -`contract_address`: Address of the contract.1503        ///1504        /// -`account_address`: Address to remove.1505        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1506        pub fn remove_from_contract_white_list(1507            origin,1508            contract_address: T::AccountId,1509            account_address: T::AccountId1510        ) -> DispatchResult {1511            let sender = ensure_signed(origin)?;15121513            #[cfg(feature = "runtime-benchmarks")]1514            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15151516            Self::ensure_contract_owned(sender, &contract_address)?;1517            <ContractWhiteList<T>>::remove(contract_address, account_address);1518            Ok(())1519        }15201521        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1522        pub fn set_collection_limits(1523            origin,1524            collection_id: u32,1525            limits: CollectionLimits,1526        ) -> DispatchResult {1527            let sender = ensure_signed(origin)?;1528            Self::check_owner_permissions(collection_id, sender.clone())?;1529            let mut target_collection = <Collection<T>>::get(collection_id);1530            let chain_limits = ChainLimit::get();1531            let climits = target_collection.limits;15321533            // collection bounds1534            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1535                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1536                Error::<T>::CollectionLimitBoundsExceeded);15371538            // token_limit   check  prev1539            ensure!(climits.token_limit > limits.token_limit && 1540                limits.token_limit <= chain_limits.account_token_ownership_limit, 1541                Error::<T>::AccountTokenLimitExceeded);15421543            target_collection.limits = limits;1544            <Collection<T>>::insert(collection_id, target_collection);15451546            Ok(())1547        } 1548    }1549}15501551impl<T: Config> Module<T> {15521553    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {15541555        let target_collection = <Collection<T>>::get(collection_id);15561557        // Limits check1558        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;15591560        // Transfer permissions check1561        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1562            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1563            Error::<T>::NoPermission);15641565        if target_collection.access == AccessMode::WhiteList {1566            Self::check_white_list(collection_id, &sender)?;1567            Self::check_white_list(collection_id, &recipient)?;1568        }15691570        match target_collection.mode1571        {1572            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient.clone())?,1573            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1574            CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient.clone())?,1575            _ => ()1576        };15771578        Self::deposit_event(RawEvent::Transfer(collection_id, item_id, sender, recipient, value));15791580        Ok(())1581    }158215831584    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15851586        // check token limit and account token limit1587        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1588        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1589        1590        Ok(())1591    }15921593    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15941595        // check token limit and account token limit1596        let total_items: u32 = ItemListIndex::get(collection_id);1597        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1598        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1599        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);16001601        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1602            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1603            Self::check_white_list(collection_id, owner)?;1604            Self::check_white_list(collection_id, sender)?;1605        }16061607        Ok(())1608    }16091610    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1611        match target_collection.mode1612        {1613            CollectionMode::NFT => {1614                if let CreateItemData::NFT(data) = data {1615                    // check sizes1616                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1617                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1618                } else {1619                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1620                }1621            },1622            CollectionMode::Fungible(_) => {1623                if let CreateItemData::Fungible(_) = data {1624                } else {1625                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1626                }1627            },1628            CollectionMode::ReFungible => {1629                if let CreateItemData::ReFungible(data) = data {16301631                    // check sizes1632                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1633                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16341635                    // Check refungibility limits1636                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1637                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1638                } else {1639                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1640                }1641            },1642            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1643        };16441645        Ok(())1646    }16471648    fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1649        match data1650        {1651            CreateItemData::NFT(data) => {1652                let item = NftItemType {1653                    owner,1654                    const_data: data.const_data,1655                    variable_data: data.variable_data1656                };16571658                Self::add_nft_item(collection_id, item)?;1659            },1660            CreateItemData::Fungible(data) => {1661                Self::add_fungible_item(collection_id, &owner, data.value)?;1662            },1663            CreateItemData::ReFungible(data) => {1664                let mut owner_list = Vec::new();1665                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});16661667                let item = ReFungibleItemType {1668                    owner: owner_list,1669                    const_data: data.const_data,1670                    variable_data: data.variable_data1671                };16721673                Self::add_refungible_item(collection_id, item)?;1674            }1675        };16761677        // call event1678        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16791680        Ok(())1681    }16821683    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {16841685        // Does new owner already have an account?1686        let mut balance: u128 = 0;1687        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1688            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1689        } 16901691        // Mint 1692        let item = FungibleItemType {1693            value: balance + value1694        };1695        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16961697        // Update balance1698        let new_balance = <Balance<T>>::get(collection_id, owner)1699            .checked_add(value)1700            .ok_or(Error::<T>::NumOverflow)?;1701        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17021703        Ok(())1704    }17051706    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1707        let current_index = <ItemListIndex>::get(collection_id)1708            .checked_add(1)1709            .ok_or(Error::<T>::NumOverflow)?;1710        let itemcopy = item.clone();17111712        let value = item.owner.first().unwrap().fraction;1713        let owner = item.owner.first().unwrap().owner.clone();17141715        Self::add_token_index(collection_id, current_index, &owner)?;17161717        <ItemListIndex>::insert(collection_id, current_index);1718        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17191720        // Update balance1721        let new_balance = <Balance<T>>::get(collection_id, &owner)1722            .checked_add(value)1723            .ok_or(Error::<T>::NumOverflow)?;1724        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17251726        Ok(())1727    }17281729    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1730        let current_index = <ItemListIndex>::get(collection_id)1731            .checked_add(1)1732            .ok_or(Error::<T>::NumOverflow)?;17331734        let item_owner = item.owner.clone();1735        Self::add_token_index(collection_id, current_index, &item.owner)?;17361737        <ItemListIndex>::insert(collection_id, current_index);1738        <NftItemList<T>>::insert(collection_id, current_index, item);17391740        // Update balance1741        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1742            .checked_add(1)1743            .ok_or(Error::<T>::NumOverflow)?;1744        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17451746        Ok(())1747    }17481749    fn burn_refungible_item(1750        collection_id: CollectionId,1751        item_id: TokenId,1752        owner: &T::AccountId,1753    ) -> DispatchResult {1754        ensure!(1755            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1756            Error::<T>::TokenNotFound1757        );1758        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1759        let rft_balance = token1760            .owner1761            .iter()1762            .filter(|&i| i.owner == *owner)1763            .next()1764            .unwrap();1765        Self::remove_token_index(collection_id, item_id, owner)?;17661767        // update balance1768        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1769            .checked_sub(rft_balance.fraction)1770            .ok_or(Error::<T>::NumOverflow)?;1771        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);17721773        // Re-create owners list with sender removed1774        let index = token1775            .owner1776            .iter()1777            .position(|i| i.owner == *owner)1778            .unwrap();1779        token.owner.remove(index);1780        let owner_count = token.owner.len();17811782        // Burn the token completely if this was the last (only) owner1783        if owner_count == 0 {1784            <ReFungibleItemList<T>>::remove(collection_id, item_id);1785        }1786        else {1787            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1788        }17891790        Ok(())1791    }17921793    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1794        ensure!(1795            <NftItemList<T>>::contains_key(collection_id, item_id),1796            Error::<T>::TokenNotFound1797        );1798        let item = <NftItemList<T>>::get(collection_id, item_id);1799        Self::remove_token_index(collection_id, item_id, &item.owner)?;18001801        // update balance1802        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1803            .checked_sub(1)1804            .ok_or(Error::<T>::NumOverflow)?;1805        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1806        <NftItemList<T>>::remove(collection_id, item_id);18071808        Ok(())1809    }18101811    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1812        ensure!(1813            <FungibleItemList<T>>::contains_key(collection_id, owner),1814            Error::<T>::TokenNotFound1815        );1816        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1817        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18181819        // update balance1820        let new_balance = <Balance<T>>::get(collection_id, owner)1821            .checked_sub(value)1822            .ok_or(Error::<T>::NumOverflow)?;1823        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18241825        if balance.value - value > 0 {1826            balance.value -= value;1827            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1828        }1829        else {1830            <FungibleItemList<T>>::remove(collection_id, owner);1831        }18321833        Ok(())1834    }18351836    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1837        ensure!(1838            <Collection<T>>::contains_key(collection_id),1839            Error::<T>::CollectionNotFound1840        );1841        Ok(())1842    }18431844    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1845        Self::collection_exists(collection_id)?;18461847        let target_collection = <Collection<T>>::get(collection_id);1848        ensure!(1849            subject == target_collection.owner,1850            Error::<T>::NoPermission1851        );18521853        Ok(())1854    }18551856    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1857        let target_collection = <Collection<T>>::get(collection_id);1858        let mut result: bool = subject == target_collection.owner;1859        let exists = <AdminList<T>>::contains_key(collection_id);18601861        if !result & exists {1862            if <AdminList<T>>::get(collection_id).contains(&subject) {1863                result = true1864            }1865        }18661867        result1868    }18691870    fn check_owner_or_admin_permissions(1871        collection_id: CollectionId,1872        subject: T::AccountId,1873    ) -> DispatchResult {1874        Self::collection_exists(collection_id)?;1875        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18761877        ensure!(1878            result,1879            Error::<T>::NoPermission1880        );1881        Ok(())1882    }18831884    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1885        let target_collection = <Collection<T>>::get(collection_id);18861887        match target_collection.mode {1888            CollectionMode::NFT => {1889                <NftItemList<T>>::get(collection_id, item_id).owner == subject1890            }1891            CollectionMode::Fungible(_) => {1892                <FungibleItemList<T>>::contains_key(collection_id, &subject)1893            }1894            CollectionMode::ReFungible => {1895                <ReFungibleItemList<T>>::get(collection_id, item_id)1896                    .owner1897                    .iter()1898                    .any(|i| i.owner == subject)1899            }1900            CollectionMode::Invalid => false,1901        }1902    }19031904    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1905        let mes = Error::<T>::AddresNotInWhiteList;1906        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);19071908        Ok(())1909    }19101911    /// Check if token exists. In case of Fungible, check if there is an entry for 1912    /// the owner in fungible balances double map1913    fn token_exists(1914        collection_id: CollectionId,1915        item_id: TokenId,1916        owner: &T::AccountId1917    ) -> DispatchResult {1918        let target_collection = <Collection<T>>::get(collection_id);1919        let exists = match target_collection.mode1920        {1921            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1922            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),1923            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1924            _ => false1925        };19261927        ensure!(exists == true, Error::<T>::TokenNotFound);1928        Ok(())1929    }19301931    fn transfer_fungible(1932        collection_id: CollectionId,1933        value: u128,1934        owner: &T::AccountId,1935        recipient: &T::AccountId,1936    ) -> DispatchResult {1937        Self::token_exists(collection_id, 0, owner)?;19381939        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1940        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19411942        // Send balance to recipient (updates balanceOf of recipient)1943        Self::add_fungible_item(collection_id, recipient, value)?;19441945        // update balanceOf of sender1946        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);19471948        // Reduce or remove sender1949        if balance.value == value {1950            <FungibleItemList<T>>::remove(collection_id, owner);1951        }1952        else {1953            balance.value -= value;1954            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1955        }19561957        Ok(())1958    }19591960    fn transfer_refungible(1961        collection_id: CollectionId,1962        item_id: TokenId,1963        value: u128,1964        owner: T::AccountId,1965        new_owner: T::AccountId,1966    ) -> DispatchResult {1967        Self::token_exists(collection_id, item_id, &owner)?;19681969        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1970        let item = full_item1971            .owner1972            .iter()1973            .filter(|i| i.owner == owner)1974            .next()1975            .ok_or(Error::<T>::NumOverflow)?;1976        let amount = item.fraction;19771978        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19791980        // update balance1981        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1982            .checked_sub(value)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(value)1988            .ok_or(Error::<T>::NumOverflow)?;1989        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19901991        let old_owner = item.owner.clone();1992        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19931994        // transfer1995        if amount == value && !new_owner_has_account {1996            // change owner1997            // new owner do not have account1998            let mut new_full_item = full_item.clone();1999            new_full_item2000                .owner2001                .iter_mut()2002                .find(|i| i.owner == owner)2003                .unwrap()2004                .owner = new_owner.clone();2005            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20062007            // update index collection2008            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2009        } else {2010            let mut new_full_item = full_item.clone();2011            new_full_item2012                .owner2013                .iter_mut()2014                .find(|i| i.owner == owner)2015                .unwrap()2016                .fraction -= value;20172018            // separate amount2019            if new_owner_has_account {2020                // new owner has account2021                new_full_item2022                    .owner2023                    .iter_mut()2024                    .find(|i| i.owner == new_owner)2025                    .unwrap()2026                    .fraction += value;2027            } else {2028                // new owner do not have account2029                new_full_item.owner.push(Ownership {2030                    owner: new_owner.clone(),2031                    fraction: value,2032                });2033                Self::add_token_index(collection_id, item_id, &new_owner)?;2034            }20352036            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2037        }20382039        Ok(())2040    }20412042    fn transfer_nft(2043        collection_id: CollectionId,2044        item_id: TokenId,2045        sender: T::AccountId,2046        new_owner: T::AccountId,2047    ) -> DispatchResult {2048        Self::token_exists(collection_id, item_id, &sender)?;20492050        let mut item = <NftItemList<T>>::get(collection_id, item_id);20512052        ensure!(2053            sender == item.owner,2054            Error::<T>::MustBeTokenOwner2055        );20562057        // update balance2058        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2059            .checked_sub(1)2060            .ok_or(Error::<T>::NumOverflow)?;2061        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20622063        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2064            .checked_add(1)2065            .ok_or(Error::<T>::NumOverflow)?;2066        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20672068        // change owner2069        let old_owner = item.owner.clone();2070        item.owner = new_owner.clone();2071        <NftItemList<T>>::insert(collection_id, item_id, item);20722073        // update index collection2074        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;20752076        Ok(())2077    }2078    2079    fn set_re_fungible_variable_data(2080        collection_id: CollectionId,2081        item_id: TokenId,2082        data: Vec<u8>2083    ) -> DispatchResult {2084        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20852086        item.variable_data = data;20872088        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20892090        Ok(())2091    }20922093    fn set_nft_variable_data(2094        collection_id: CollectionId,2095        item_id: TokenId,2096        data: Vec<u8>2097    ) -> DispatchResult {2098        let mut item = <NftItemList<T>>::get(collection_id, item_id);2099        2100        item.variable_data = data;21012102        <NftItemList<T>>::insert(collection_id, item_id, item);2103        2104        Ok(())2105    }21062107    fn init_collection(item: &CollectionType<T::AccountId>) {2108        // check params2109        assert!(2110            item.decimal_points <= MAX_DECIMAL_POINTS,2111            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2112        );2113        assert!(2114            item.name.len() <= 64,2115            "Collection name can not be longer than 63 char"2116        );2117        assert!(2118            item.name.len() <= 256,2119            "Collection description can not be longer than 255 char"2120        );2121        assert!(2122            item.token_prefix.len() <= 16,2123            "Token prefix can not be longer than 15 char"2124        );21252126        // Generate next collection ID2127        let next_id = CreatedCollectionCount::get()2128            .checked_add(1)2129            .unwrap();21302131        CreatedCollectionCount::put(next_id);2132    }21332134    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2135        let current_index = <ItemListIndex>::get(collection_id)2136            .checked_add(1)2137            .unwrap();21382139        let item_owner = item.owner.clone();2140        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21412142        <ItemListIndex>::insert(collection_id, current_index);21432144        // Update balance2145        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2146            .checked_add(1)2147            .unwrap();2148        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2149    }21502151    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2152        let current_index = <ItemListIndex>::get(collection_id)2153            .checked_add(1)2154            .unwrap();21552156        Self::add_token_index(collection_id, current_index, owner).unwrap();21572158        <ItemListIndex>::insert(collection_id, current_index);21592160        // Update balance2161        let new_balance = <Balance<T>>::get(collection_id, owner)2162            .checked_add(item.value)2163            .unwrap();2164        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2165    }21662167    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2168        let current_index = <ItemListIndex>::get(collection_id)2169            .checked_add(1)2170            .unwrap();21712172        let value = item.owner.first().unwrap().fraction;2173        let owner = item.owner.first().unwrap().owner.clone();21742175        Self::add_token_index(collection_id, current_index, &owner).unwrap();21762177        <ItemListIndex>::insert(collection_id, current_index);21782179        // Update balance2180        let new_balance = <Balance<T>>::get(collection_id, &owner)2181            .checked_add(value)2182            .unwrap();2183        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2184    }21852186    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {21872188        // add to account limit2189        if <AccountItemCount<T>>::contains_key(owner) {21902191            // bound Owned tokens by a single address2192            let count = <AccountItemCount<T>>::get(owner);2193            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21942195            <AccountItemCount<T>>::insert(owner.clone(), count2196                .checked_add(1)2197                .ok_or(Error::<T>::NumOverflow)?);2198        }2199        else {2200            <AccountItemCount<T>>::insert(owner.clone(), 1);2201        }22022203        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2204        if list_exists {2205            let mut list = <AddressTokens<T>>::get(collection_id, owner);2206            let item_contains = list.contains(&item_index.clone());22072208            if !item_contains {2209                list.push(item_index.clone());2210            }22112212            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2213        } else {2214            let mut itm = Vec::new();2215            itm.push(item_index.clone());2216            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2217        }22182219        Ok(())2220    }22212222    fn remove_token_index(2223        collection_id: CollectionId,2224        item_index: TokenId,2225        owner: &T::AccountId,2226    ) -> DispatchResult {22272228        // update counter2229        <AccountItemCount<T>>::insert(owner.clone(), 2230            <AccountItemCount<T>>::get(owner)2231            .checked_sub(1)2232            .ok_or(Error::<T>::NumOverflow)?);223322342235        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2236        if list_exists {2237            let mut list = <AddressTokens<T>>::get(collection_id, owner);2238            let item_contains = list.contains(&item_index.clone());22392240            if item_contains {2241                list.retain(|&item| item != item_index);2242                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2243            }2244        }22452246        Ok(())2247    }22482249    fn move_token_index(2250        collection_id: CollectionId,2251        item_index: TokenId,2252        old_owner: &T::AccountId,2253        new_owner: &T::AccountId,2254    ) -> DispatchResult {2255        Self::remove_token_index(collection_id, item_index, old_owner)?;2256        Self::add_token_index(collection_id, item_index, new_owner)?;22572258        Ok(())2259    }2260    2261    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2262        if <ContractOwner<T>>::contains_key(contract.clone()) {2263            let owner = <ContractOwner<T>>::get(contract);2264            ensure!(account == owner, Error::<T>::NoPermission);2265        } else {2266            fail!(Error::<T>::NoPermission);2267        }22682269        Ok(())2270    }2271}22722273////////////////////////////////////////////////////////////////////////////////////////////////////2274// Economic models2275// #region22762277/// Fee multiplier.2278pub type Multiplier = FixedU128;22792280type BalanceOf<T> = <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::Balance;22812282/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2283/// in the queue.2284#[derive(Encode, Decode, Clone, Eq, PartialEq)]2285pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);22862287impl<T: Config + Send + Sync> sp_std::fmt::Debug 2288    for ChargeTransactionPayment<T>2289{2290	#[cfg(feature = "std")]2291	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2292		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2293	}2294	#[cfg(not(feature = "std"))]2295	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2296		Ok(())2297	}2298}22992300impl<T: Config> ChargeTransactionPayment<T>2301where2302    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2303    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2304    T::AccountId: AsRef<[u8]>,2305    T::AccountId: UncheckedFrom<T::Hash>,2306{2307    fn traditional_fee(2308        len: usize,2309        info: &DispatchInfoOf<T::Call>,2310        tip: BalanceOf<T>,2311    ) -> BalanceOf<T>2312    where2313        T::Call: Dispatchable<Info = DispatchInfo>,2314    {2315        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2316    }23172318	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2319        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2320        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2321        let len_saturation = max_block_length as u64 / (len as u64).max(1);2322        let coefficient: BalanceOf<T> = weight_saturation2323            .min(len_saturation)2324            .saturated_into::<BalanceOf<T>>();2325        final_fee2326            .saturating_mul(coefficient)2327            .saturated_into::<TransactionPriority>()2328    }23292330    fn withdraw_fee(2331        &self,2332        who: &T::AccountId,2333        call: &T::Call,2334        info: &DispatchInfoOf<T::Call>,2335        len: usize,2336	) -> Result<2337		(2338			BalanceOf<T>,2339			<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2340		),2341		TransactionValidityError,2342	> {2343        let tip = self.0;23442345        // Set fee based on call type. Creating collection costs 1 Unique.2346        // All other transactions have traditional fees so far2347        // let fee = match call.is_sub_type() {2348        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2349        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2350        //                                                 // _ => <BalanceOf<T>>::from(100)2351        // };2352        let fee = Self::traditional_fee(len, info, tip);23532354        // Only mess with balances if fee is not zero.2355        if fee.is_zero() {2356            return <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2357			.map(|i| (fee, i));2358        }23592360        // Determine who is paying transaction fee based on ecnomic model2361        // Parse call to extract collection ID and access collection sponsor2362        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2363            Some(Call::create_item(collection_id, _owner, _properties)) => {23642365                // sponsor timeout2366                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;23672368                let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;2369                let mut sponsored = true;2370                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2371                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2372                    let limit_time = last_tx_block + limit.into();2373                    if block_number <= limit_time {2374                        sponsored = false;2375                    }2376                }2377                if sponsored {2378                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2379                }23802381                // check free create limit2382                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2383                   (<Collection<T>>::get(collection_id).sponsor_confirmed) &&2384                   (sponsored)2385                {2386                    <Collection<T>>::get(collection_id).sponsor2387                } else {2388                    T::AccountId::default()2389                }2390            }2391            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2392                2393                let mut sponsor_transfer = false;2394                if <Collection<T>>::get(collection_id).sponsor_confirmed {23952396                    let collection_limits = <Collection<T>>::get(collection_id).limits;2397                    let collection_mode = <Collection<T>>::get(collection_id).mode;2398    2399                    // sponsor timeout2400                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2401                    sponsor_transfer = match collection_mode {2402                        CollectionMode::NFT => {2403    2404                            // get correct limit2405                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2406                                collection_limits.sponsor_transfer_timeout2407                            } else {2408                                ChainLimit::get().nft_sponsor_transfer_timeout2409                            };2410    2411                            let mut sponsored = true;2412                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2413                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2414                                let limit_time = last_tx_block + limit.into();2415                                if block_number <= limit_time {2416                                    sponsored = false;2417                                }2418                            }2419                            if sponsored {2420                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2421                            }24222423                            sponsored2424                        }2425                        CollectionMode::Fungible(_) => {2426    2427                            // get correct limit2428                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2429                                collection_limits.sponsor_transfer_timeout2430                            } else {2431                                ChainLimit::get().fungible_sponsor_transfer_timeout2432                            };2433    2434                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2435                            let mut sponsored = true;2436                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2437                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2438                                let limit_time = last_tx_block + limit.into();2439                                if block_number <= limit_time {2440                                    sponsored = false;2441                                }2442                            }2443                            if sponsored {2444                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2445                            }24462447                            sponsored2448                        }2449                        CollectionMode::ReFungible => {2450    2451                            // get correct limit2452                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2453                                collection_limits.sponsor_transfer_timeout2454                            } else {2455                                ChainLimit::get().refungible_sponsor_transfer_timeout2456                            };2457    2458                            let mut sponsored = true;2459                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2460                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2461                                let limit_time = last_tx_block + limit.into();2462                                if block_number <= limit_time {2463                                    sponsored = false;2464                                }2465                            }2466                            if sponsored {2467                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2468                            }24692470                            sponsored2471                        }2472                        _ => {2473                            false2474                        },2475                    };2476                }24772478                if !sponsor_transfer {2479                    T::AccountId::default()2480                } else {2481                    <Collection<T>>::get(collection_id).sponsor2482                }2483            }24842485            _ => T::AccountId::default(),2486        };24872488        // Sponsor smart contracts2489        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {24902491            // On instantiation: set the contract owner2492            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {24932494                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2495                    &who,2496                    code_hash,2497                    salt,2498                );2499                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());25002501                T::AccountId::default()2502            },25032504            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2505            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {25062507                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());25082509                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2510                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2511                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2512                  2513                if !owned_contract && white_list_enabled {2514                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2515                        return Err(InvalidTransaction::Call.into());2516                    }2517                }25182519                let mut sponsor_transfer = false;2520                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2521                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2522                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2523                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2524                    let limit_time = last_tx_block + rate_limit;25252526                    if block_number >= limit_time {2527                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2528                        sponsor_transfer = true;2529                    }2530                } else {2531                    sponsor_transfer = false;2532                }2533               2534                2535                let mut sp = T::AccountId::default();2536                if sponsor_transfer {2537                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2538                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2539                            sp = called_contract;2540                        }2541                    }2542                }25432544                sp2545            },25462547            _ => sponsor,2548        };25492550        let mut who_pays_fee: T::AccountId = sponsor.clone();2551        if sponsor == T::AccountId::default() {2552            who_pays_fee = who.clone();2553        }25542555		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2556			.map(|i| (fee, i))2557    }2558}255925602561impl<T: Config + Send + Sync> SignedExtension2562    for ChargeTransactionPayment<T>2563where2564    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2565    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2566    T::AccountId: AsRef<[u8]>,2567    T::AccountId: UncheckedFrom<T::Hash>,2568{2569    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2570    type AccountId = T::AccountId;2571    type Call = T::Call;2572    type AdditionalSigned = ();2573    type Pre = (2574        // tip2575        BalanceOf<T>,2576        // who pays fee2577        Self::AccountId,2578		// imbalance resulting from withdrawing the fee2579		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2580    );2581    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2582        Ok(())2583    }25842585    fn validate(2586        &self,2587        who: &Self::AccountId,2588        call: &Self::Call,2589        info: &DispatchInfoOf<Self::Call>,2590        len: usize,2591    ) -> TransactionValidity {2592		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2593		Ok(ValidTransaction {2594			priority: Self::get_priority(len, info, fee),2595			..Default::default()2596		})2597    }25982599    fn pre_dispatch(2600        self,2601        who: &Self::AccountId,2602        call: &Self::Call,2603        info: &DispatchInfoOf<Self::Call>,2604        len: usize,2605    ) -> Result<Self::Pre, TransactionValidityError> {2606        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2607        Ok((self.0, who.clone(), imbalance))2608    }26092610    fn post_dispatch(2611        pre: Self::Pre,2612        info: &DispatchInfoOf<Self::Call>,2613        post_info: &PostDispatchInfoOf<Self::Call>,2614        len: usize,2615        _result: &DispatchResult,2616    ) -> Result<(), TransactionValidityError> {2617		let (tip, who, imbalance) = pre;2618		let actual_fee = transaction_payment::Module::<T>::compute_actual_fee(2619			len as u32,2620			info,2621			post_info,2622			tip,2623		);2624		<T as transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2625		Ok(())2626    }2627}26282629// #endregion