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

difftreelog

source

pallets/nft/src/lib.rs97.0 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        /// Tried to enable permissions which are only permitted to be disabled386        OwnerPermissionsCantBeReturned,387        /// Schema data size limit bound exceeded388        SchemaDataLimitExceeded,389        /// Maximum refungibility exceeded390        WrongRefungiblePieces391	}392}393394pub trait Config: system::Config + Sized + transaction_payment::Config + pallet_contracts::Config {395    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;396397    /// Weight information for extrinsics in this pallet.398	type WeightInfo: WeightInfo;399}400401#[cfg(feature = "runtime-benchmarks")]402mod benchmarking;403404// #endregion405406decl_storage! {407    trait Store for Module<T: Config> as Nft {408409        // Private members410        NextCollectionID: CollectionId;411        CreatedCollectionCount: u32;412        ChainVersion: u64;413        ItemListIndex: map hasher(identity) CollectionId => TokenId;414415        // Chain limits struct416        pub ChainLimit get(fn chain_limit) config(): ChainLimits;417418        // Bound counters419        CollectionCount: u32;420        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;421422        // Basic collections423        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;424        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;425        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;426427        /// Balance owner per collection map428        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;429430        /// second parameter: item id + owner account id + spender account id431        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;432433        /// Item collections434        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;435        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;436        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;437438        /// Index list439        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;440441        /// Tokens transfer baskets442        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;443        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;444        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;445        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;446447        // Contract Sponsorship and Ownership448        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;449        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;450        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;451        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;452        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 453        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 454    }455    add_extra_genesis {456        build(|config: &GenesisConfig<T>| {457            // Modification of storage458            for (_num, _c) in &config.collection {459                <Module<T>>::init_collection(_c);460            }461462            for (_num, _c, _i) in &config.nft_item_id {463                <Module<T>>::init_nft_token(*_c, _i);464            }465466            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {467                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);468            }469470            for (_num, _c, _i) in &config.refungible_item_id {471                <Module<T>>::init_refungible_token(*_c, _i);472            }473        })474    }475}476477decl_event!(478    pub enum Event<T>479    where480        AccountId = <T as system::Config>::AccountId,481    {482        /// New collection was created483        /// 484        /// # Arguments485        /// 486        /// * collection_id: Globally unique identifier of newly created collection.487        /// 488        /// * mode: [CollectionMode] converted into u8.489        /// 490        /// * account_id: Collection owner.491        Created(CollectionId, u8, AccountId),492493        /// New item was created.494        /// 495        /// # Arguments496        /// 497        /// * collection_id: Id of the collection where item was created.498        /// 499        /// * item_id: Id of an item. Unique within the collection.500        ItemCreated(CollectionId, TokenId),501502        /// Collection item was burned.503        /// 504        /// # Arguments505        /// 506        /// collection_id.507        /// 508        /// item_id: Identifier of burned NFT.509        ItemDestroyed(CollectionId, TokenId),510    }511);512513decl_module! {514    pub struct Module<T: Config> for enum Call 515    where 516        origin: T::Origin517    {518        fn deposit_event() = default;519        type Error = Error<T>;520521        fn on_initialize(now: T::BlockNumber) -> Weight {522523            if ChainVersion::get() < 2524            {525                let value = NextCollectionID::get();526                CreatedCollectionCount::put(value);527                ChainVersion::put(2);528            }529530            0531        }532533        /// 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.534        /// 535        /// # Permissions536        /// 537        /// * Anyone.538        /// 539        /// # Arguments540        /// 541        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.542        /// 543        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.544        /// 545        /// * token_prefix: UTF-8 string with token prefix.546        /// 547        /// * mode: [CollectionMode] collection type and type dependent data.548        // returns collection ID549        #[weight = <T as Config>::WeightInfo::create_collection()]550        pub fn create_collection(origin,551                                 collection_name: Vec<u16>,552                                 collection_description: Vec<u16>,553                                 token_prefix: Vec<u8>,554                                 mode: CollectionMode) -> DispatchResult {555556            // Anyone can create a collection557            let who = ensure_signed(origin)?;558559            let decimal_points = match mode {560                CollectionMode::Fungible(points) => points,561                _ => 0562            };563564            // bound Total number of collections565            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);566567            // check params568            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);569            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);570            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);571            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);572573            // Generate next collection ID574            let next_id = CreatedCollectionCount::get()575                .checked_add(1)576                .ok_or(Error::<T>::NumOverflow)?;577578            // bound counter579            let total = CollectionCount::get()580                .checked_add(1)581                .ok_or(Error::<T>::NumOverflow)?;582583            CreatedCollectionCount::put(next_id);584            CollectionCount::put(total);585586            // Create new collection587            let new_collection = CollectionType {588                owner: who.clone(),589                name: collection_name,590                mode: mode.clone(),591                mint_mode: false,592                access: AccessMode::Normal,593                description: collection_description,594                decimal_points: decimal_points,595                token_prefix: token_prefix,596                offchain_schema: Vec::new(),597                schema_version: SchemaVersion::ImageURL,598                sponsor: T::AccountId::default(),599                sponsor_confirmed: false,600                variable_on_chain_schema: Vec::new(),601                const_on_chain_schema: Vec::new(),602                limits: CollectionLimits::default()603            };604605            // Add new collection to map606            <Collection<T>>::insert(next_id, new_collection);607608            // call event609            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));610611            Ok(())612        }613614        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.615        /// 616        /// # Permissions617        /// 618        /// * Collection Owner.619        /// 620        /// # Arguments621        /// 622        /// * collection_id: collection to destroy.623        #[weight = <T as Config>::WeightInfo::destroy_collection()]624        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {625626            let sender = ensure_signed(origin)?;627            Self::check_owner_permissions(collection_id, sender)?;628629            let mut target_collection = <Collection<T>>::get(collection_id);630            if !target_collection.limits.owner_can_destroy {631                fail!(Error::<T>::NoPermission);632            }633634            <AddressTokens<T>>::remove_prefix(collection_id);635            <Allowances<T>>::remove_prefix(collection_id);636            <Balance<T>>::remove_prefix(collection_id);637            <ItemListIndex>::remove(collection_id);638            <AdminList<T>>::remove(collection_id);639            <Collection<T>>::remove(collection_id);640            <WhiteList<T>>::remove_prefix(collection_id);641642            <NftItemList<T>>::remove_prefix(collection_id);643            <FungibleItemList<T>>::remove_prefix(collection_id);644            <ReFungibleItemList<T>>::remove_prefix(collection_id);645646            <NftTransferBasket<T>>::remove_prefix(collection_id);647            <FungibleTransferBasket<T>>::remove_prefix(collection_id);648            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);649650            if CollectionCount::get() > 0651            {652                // bound couter653                let total = CollectionCount::get()654                    .checked_sub(1)655                    .ok_or(Error::<T>::NumOverflow)?;656657                CollectionCount::put(total);658            }659660            Ok(())661        }662663        /// Add an address to white list.664        /// 665        /// # Permissions666        /// 667        /// * Collection Owner668        /// * Collection Admin669        /// 670        /// # Arguments671        /// 672        /// * collection_id.673        /// 674        /// * address.675        #[weight = <T as Config>::WeightInfo::add_to_white_list()]676        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{677678            let sender = ensure_signed(origin)?;679            Self::check_owner_or_admin_permissions(collection_id, sender)?;680681            <WhiteList<T>>::insert(collection_id, address, true);682            683            Ok(())684        }685686        /// Remove an address from white list.687        /// 688        /// # Permissions689        /// 690        /// * Collection Owner691        /// * Collection Admin692        /// 693        /// # Arguments694        /// 695        /// * collection_id.696        /// 697        /// * address.698        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]699        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{700701            let sender = ensure_signed(origin)?;702            Self::check_owner_or_admin_permissions(collection_id, sender)?;703704            <WhiteList<T>>::remove(collection_id, address);705706            Ok(())707        }708709        /// Toggle between normal and white list access for the methods with access for `Anyone`.710        /// 711        /// # Permissions712        /// 713        /// * Collection Owner.714        /// 715        /// # Arguments716        /// 717        /// * collection_id.718        /// 719        /// * mode: [AccessMode]720        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]721        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult722        {723            let sender = ensure_signed(origin)?;724725            Self::check_owner_permissions(collection_id, sender)?;726            let mut target_collection = <Collection<T>>::get(collection_id);727            target_collection.access = mode;728            <Collection<T>>::insert(collection_id, target_collection);729730            Ok(())731        }732733        /// Allows Anyone to create tokens if:734        /// * White List is enabled, and735        /// * Address is added to white list, and736        /// * This method was called with True parameter737        /// 738        /// # Permissions739        /// * Collection Owner740        ///741        /// # Arguments742        /// 743        /// * collection_id.744        /// 745        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.746        #[weight = <T as Config>::WeightInfo::set_mint_permission()]747        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult748        {749            let sender = ensure_signed(origin)?;750751            Self::check_owner_permissions(collection_id, sender)?;752            let mut target_collection = <Collection<T>>::get(collection_id);753            target_collection.mint_mode = mint_permission;754            <Collection<T>>::insert(collection_id, target_collection);755756            Ok(())757        }758759        /// Change the owner of the collection.760        /// 761        /// # Permissions762        /// 763        /// * Collection Owner.764        /// 765        /// # Arguments766        /// 767        /// * collection_id.768        /// 769        /// * new_owner.770        #[weight = <T as Config>::WeightInfo::change_collection_owner()]771        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {772773            let sender = ensure_signed(origin)?;774            Self::check_owner_permissions(collection_id, sender)?;775            let mut target_collection = <Collection<T>>::get(collection_id);776            target_collection.owner = new_owner;777            <Collection<T>>::insert(collection_id, target_collection);778779            Ok(())780        }781782        /// Adds an admin of the Collection.783        /// 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. 784        /// 785        /// # Permissions786        /// 787        /// * Collection Owner.788        /// * Collection Admin.789        /// 790        /// # Arguments791        /// 792        /// * collection_id: ID of the Collection to add admin for.793        /// 794        /// * new_admin_id: Address of new admin to add.795        #[weight = <T as Config>::WeightInfo::add_collection_admin()]796        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {797798            let sender = ensure_signed(origin)?;799            Self::check_owner_or_admin_permissions(collection_id, sender)?;800            let mut admin_arr: Vec<T::AccountId> = Vec::new();801802            if <AdminList<T>>::contains_key(collection_id)803            {804                admin_arr = <AdminList<T>>::get(collection_id);805                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);806            }807808            // Number of collection admins809            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);810811            admin_arr.push(new_admin_id);812            <AdminList<T>>::insert(collection_id, admin_arr);813814            Ok(())815        }816817        /// 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.818        ///819        /// # Permissions820        /// 821        /// * Collection Owner.822        /// * Collection Admin.823        /// 824        /// # Arguments825        /// 826        /// * collection_id: ID of the Collection to remove admin for.827        /// 828        /// * account_id: Address of admin to remove.829        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]830        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {831832            let sender = ensure_signed(origin)?;833            Self::check_owner_or_admin_permissions(collection_id, sender)?;834            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);835836            let mut admin_arr = <AdminList<T>>::get(collection_id);837            admin_arr.retain(|i| *i != account_id);838            <AdminList<T>>::insert(collection_id, admin_arr);839840            Ok(())841        }842843        /// # Permissions844        /// 845        /// * Collection Owner846        /// 847        /// # Arguments848        /// 849        /// * collection_id.850        /// 851        /// * new_sponsor.852        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]853        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {854855            let sender = ensure_signed(origin)?;856            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);857858            let mut target_collection = <Collection<T>>::get(collection_id);859            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);860861            target_collection.sponsor = new_sponsor;862            target_collection.sponsor_confirmed = false;863            <Collection<T>>::insert(collection_id, target_collection);864865            Ok(())866        }867868        /// # Permissions869        /// 870        /// * Sponsor.871        /// 872        /// # Arguments873        /// 874        /// * collection_id.875        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]876        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {877878            let sender = ensure_signed(origin)?;879            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);880881            let mut target_collection = <Collection<T>>::get(collection_id);882            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);883884            target_collection.sponsor_confirmed = true;885            <Collection<T>>::insert(collection_id, target_collection);886887            Ok(())888        }889890        /// Switch back to pay-per-own-transaction model.891        ///892        /// # Permissions893        ///894        /// * Collection owner.895        /// 896        /// # Arguments897        /// 898        /// * collection_id.899        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]900        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {901902            let sender = ensure_signed(origin)?;903            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);904905            let mut target_collection = <Collection<T>>::get(collection_id);906            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);907908            target_collection.sponsor = T::AccountId::default();909            target_collection.sponsor_confirmed = false;910            <Collection<T>>::insert(collection_id, target_collection);911912            Ok(())913        }914915        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.916        /// 917        /// # Permissions918        /// 919        /// * Collection Owner.920        /// * Collection Admin.921        /// * Anyone if922        ///     * White List is enabled, and923        ///     * Address is added to white list, and924        ///     * MintPermission is enabled (see SetMintPermission method)925        /// 926        /// # Arguments927        /// 928        /// * collection_id: ID of the collection.929        /// 930        /// * owner: Address, initial owner of the NFT.931        ///932        /// * data: Token data to store on chain.933        // #[weight =934        // (130_000_000 as Weight)935        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))936        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))937        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]938939        #[weight = <T as Config>::WeightInfo::create_item(data.len())]940        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {941942            let sender = ensure_signed(origin)?;943944            Self::collection_exists(collection_id)?;945946            let target_collection = <Collection<T>>::get(collection_id);947948            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;949            Self::validate_create_item_args(&target_collection, &data)?;950            Self::create_item_no_validation(collection_id, owner, data)?;951952            Ok(())953        }954955        /// This method creates multiple instances of NFT Collection created with CreateCollection method.956        /// 957        /// # Permissions958        /// 959        /// * Collection Owner.960        /// * Collection Admin.961        /// * Anyone if962        ///     * White List is enabled, and963        ///     * Address is added to white list, and964        ///     * MintPermission is enabled (see SetMintPermission method)965        /// 966        /// # Arguments967        /// 968        /// * collection_id: ID of the collection.969        /// 970        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].971        /// 972        /// * owner: Address, initial owner of the NFT.973        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()974                               .map(|data| { data.len() })975                               .sum())]976        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {977978            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);979            let sender = ensure_signed(origin)?;980981            Self::collection_exists(collection_id)?;982            let target_collection = <Collection<T>>::get(collection_id);983984            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;985986            for data in &items_data {987                Self::validate_create_item_args(&target_collection, data)?;988            }989            for data in &items_data {990                Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?;991            }992993            Ok(())994        }995996        /// Destroys a concrete instance of NFT.997        /// 998        /// # Permissions999        /// 1000        /// * Collection Owner.1001        /// * Collection Admin.1002        /// * Current NFT Owner.1003        /// 1004        /// # Arguments1005        /// 1006        /// * collection_id: ID of the collection.1007        /// 1008        /// * item_id: ID of NFT to burn.1009        #[weight = <T as Config>::WeightInfo::burn_item()]1010        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10111012            let sender = ensure_signed(origin)?;1013            Self::collection_exists(collection_id)?;10141015            // Transfer permissions check1016            let target_collection = <Collection<T>>::get(collection_id);1017            ensure!(1018                Self::is_item_owner(sender.clone(), collection_id, item_id) ||1019                (1020                    target_collection.limits.owner_can_transfer &&1021                    Self::is_owner_or_admin_permissions(collection_id, sender.clone())1022                ),1023                Error::<T>::NoPermission1024            );10251026            if target_collection.access == AccessMode::WhiteList {1027                Self::check_white_list(collection_id, &sender)?;1028            }10291030            match target_collection.mode1031            {1032                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1033                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1034                CollectionMode::ReFungible  => Self::burn_refungible_item(collection_id, item_id, &sender)?,1035                _ => ()1036            };10371038            // call event1039            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10401041            Ok(())1042        }10431044        /// Change ownership of the token.1045        /// 1046        /// # Permissions1047        /// 1048        /// * Collection Owner1049        /// * Collection Admin1050        /// * Current NFT owner1051        ///1052        /// # Arguments1053        /// 1054        /// * recipient: Address of token recipient.1055        /// 1056        /// * collection_id.1057        /// 1058        /// * item_id: ID of the item1059        ///     * Non-Fungible Mode: Required.1060        ///     * Fungible Mode: Ignored.1061        ///     * Re-Fungible Mode: Required.1062        /// 1063        /// * value: Amount to transfer.1064        ///     * Non-Fungible Mode: Ignored1065        ///     * Fungible Mode: Must specify transferred amount1066        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1067        #[weight = <T as Config>::WeightInfo::transfer()]1068        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1069            let sender = ensure_signed(origin)?;1070            Self::transfer_internal(sender, recipient, collection_id, item_id, value)1071        }10721073        /// Set, change, or remove approved address to transfer the ownership of the NFT.1074        /// 1075        /// # Permissions1076        /// 1077        /// * Collection Owner1078        /// * Collection Admin1079        /// * Current NFT owner1080        /// 1081        /// # Arguments1082        /// 1083        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1084        /// 1085        /// * collection_id.1086        /// 1087        /// * item_id: ID of the item.1088        #[weight = <T as Config>::WeightInfo::approve()]1089        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {10901091            let sender = ensure_signed(origin)?;10921093            Self::collection_exists(collection_id)?;1094            Self::token_exists(collection_id, item_id, &sender)?;10951096            // Transfer permissions check1097            let target_collection = <Collection<T>>::get(collection_id);1098            ensure!(1099                Self::is_item_owner(sender.clone(), collection_id, item_id) ||1100                (1101                    target_collection.limits.owner_can_transfer &&1102                    Self::is_owner_or_admin_permissions(collection_id, sender.clone())1103                ),1104                Error::<T>::NoPermission1105            );11061107            if target_collection.access == AccessMode::WhiteList {1108                Self::check_white_list(collection_id, &sender)?;1109                Self::check_white_list(collection_id, &spender)?;1110            }11111112            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1113            let mut allowance: u128 = amount;1114            if allowance_exists {1115                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1116            }1117            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11181119            Ok(())1120        }1121        1122        /// 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.1123        /// 1124        /// # Permissions1125        /// * Collection Owner1126        /// * Collection Admin1127        /// * Current NFT owner1128        /// * Address approved by current NFT owner1129        /// 1130        /// # Arguments1131        /// 1132        /// * from: Address that owns token.1133        /// 1134        /// * recipient: Address of token recipient.1135        /// 1136        /// * collection_id.1137        /// 1138        /// * item_id: ID of the item.1139        /// 1140        /// * value: Amount to transfer.1141        #[weight = <T as Config>::WeightInfo::transfer_from()]1142        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11431144            let sender = ensure_signed(origin)?;1145            let mut appoved_transfer = false;11461147            // Check approval1148            let mut approval: u128 = 0;1149            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1150                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1151                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1152                appoved_transfer = true;1153            }11541155            let target_collection = <Collection<T>>::get(collection_id);11561157            // Limits check1158            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11591160            // Transfer permissions check         1161            ensure!(1162                appoved_transfer || 1163                (1164                    target_collection.limits.owner_can_transfer &&1165                    Self::is_owner_or_admin_permissions(collection_id, sender.clone())1166                ),1167                Error::<T>::NoPermission1168            );11691170            if target_collection.access == AccessMode::WhiteList {1171                Self::check_white_list(collection_id, &sender)?;1172                Self::check_white_list(collection_id, &recipient)?;1173            }11741175            // Reduce approval by transferred amount or remove if remaining approval drops to 01176            if approval.checked_sub(value).unwrap_or(0) > 0 {1177                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1178            }1179            else {1180                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1181            }11821183            match target_collection.mode1184            {1185                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1186                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1187                CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1188                _ => ()1189            };11901191            Ok(())1192        }11931194        // #[weight = 0]1195        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11961197        //     // let no_perm_mes = "You do not have permissions to modify this collection";1198        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1199        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1200        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);12011202        //     // // on_nft_received  call12031204        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;12051206        //     Ok(())1207        // }12081209        /// Set off-chain data schema.1210        /// 1211        /// # Permissions1212        /// 1213        /// * Collection Owner1214        /// * Collection Admin1215        /// 1216        /// # Arguments1217        /// 1218        /// * collection_id.1219        /// 1220        /// * schema: String representing the offchain data schema.1221        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1222        pub fn set_variable_meta_data (1223            origin,1224            collection_id: CollectionId,1225            item_id: TokenId,1226            data: Vec<u8>1227        ) -> DispatchResult {1228            let sender = ensure_signed(origin)?;1229            1230            Self::collection_exists(collection_id)?;1231            Self::token_exists(collection_id, item_id, &sender)?;12321233            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12341235            // Modify permissions check1236            let target_collection = <Collection<T>>::get(collection_id);1237            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1238                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1239                Error::<T>::NoPermission);12401241            match target_collection.mode1242            {1243                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1244                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1245                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1246                _ => fail!(Error::<T>::UnexpectedCollectionType)1247            };12481249            Ok(())1250        }1251 1252        /// Set schema standard1253        /// ImageURL1254        /// Unique1255        /// 1256        /// # Permissions1257        /// 1258        /// * Collection Owner1259        /// * Collection Admin1260        /// 1261        /// # Arguments1262        /// 1263        /// * collection_id.1264        /// 1265        /// * schema: SchemaVersion: enum1266        #[weight = <T as Config>::WeightInfo::set_schema_version()]1267        pub fn set_schema_version(1268            origin,1269            collection_id: CollectionId,1270            version: SchemaVersion1271        ) -> DispatchResult {1272            let sender = ensure_signed(origin)?;1273            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1274            let mut target_collection = <Collection<T>>::get(collection_id);1275            target_collection.schema_version = version;1276            <Collection<T>>::insert(collection_id, target_collection);12771278            Ok(())1279        }12801281        /// Set off-chain data schema.1282        /// 1283        /// # Permissions1284        /// 1285        /// * Collection Owner1286        /// * Collection Admin1287        /// 1288        /// # Arguments1289        /// 1290        /// * collection_id.1291        /// 1292        /// * schema: String representing the offchain data schema.1293        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1294        pub fn set_offchain_schema(1295            origin,1296            collection_id: CollectionId,1297            schema: Vec<u8>1298        ) -> DispatchResult {1299            let sender = ensure_signed(origin)?;1300            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13011302            // check schema limit1303            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");13041305            let mut target_collection = <Collection<T>>::get(collection_id);1306            target_collection.offchain_schema = schema;1307            <Collection<T>>::insert(collection_id, target_collection);13081309            Ok(())1310        }13111312        /// Set const on-chain data schema.1313        /// 1314        /// # Permissions1315        /// 1316        /// * Collection Owner1317        /// * Collection Admin1318        /// 1319        /// # Arguments1320        /// 1321        /// * collection_id.1322        /// 1323        /// * schema: String representing the const on-chain data schema.1324        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1325        pub fn set_const_on_chain_schema (1326            origin,1327            collection_id: CollectionId,1328            schema: Vec<u8>1329        ) -> DispatchResult {1330            let sender = ensure_signed(origin)?;1331            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13321333            // check schema limit1334            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");13351336            let mut target_collection = <Collection<T>>::get(collection_id);1337            target_collection.const_on_chain_schema = schema;1338            <Collection<T>>::insert(collection_id, target_collection);13391340            Ok(())1341        }13421343        /// Set variable on-chain data schema.1344        /// 1345        /// # Permissions1346        /// 1347        /// * Collection Owner1348        /// * Collection Admin1349        /// 1350        /// # Arguments1351        /// 1352        /// * collection_id.1353        /// 1354        /// * schema: String representing the variable on-chain data schema.1355        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1356        pub fn set_variable_on_chain_schema (1357            origin,1358            collection_id: CollectionId,1359            schema: Vec<u8>1360        ) -> DispatchResult {1361            let sender = ensure_signed(origin)?;1362            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13631364            // check schema limit1365            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");13661367            let mut target_collection = <Collection<T>>::get(collection_id);1368            target_collection.variable_on_chain_schema = schema;1369            <Collection<T>>::insert(collection_id, target_collection);13701371            Ok(())1372        }13731374        // Sudo permissions function1375        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1376        pub fn set_chain_limits(1377            origin,1378            limits: ChainLimits1379        ) -> DispatchResult {13801381            #[cfg(not(feature = "runtime-benchmarks"))]1382            ensure_root(origin)?;13831384            <ChainLimit>::put(limits);1385            Ok(())1386        }13871388        /// Enable smart contract self-sponsoring.1389        /// 1390        /// # Permissions1391        /// 1392        /// * Contract Owner1393        /// 1394        /// # Arguments1395        /// 1396        /// * contract address1397        /// * enable flag1398        /// 1399        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1400        pub fn enable_contract_sponsoring(1401            origin,1402            contract_address: T::AccountId,1403            enable: bool1404        ) -> DispatchResult {14051406            let sender = ensure_signed(origin)?;14071408            #[cfg(feature = "runtime-benchmarks")]1409            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14101411            Self::ensure_contract_owned(sender, &contract_address)?;14121413            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1414            Ok(())1415        }14161417        /// Set the rate limit for contract sponsoring to specified number of blocks.1418        /// 1419        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1420        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1421        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1422        /// from contract endowment if there are at least B blocks between such transactions. 1423        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1424        /// 1425        /// # Permissions1426        /// 1427        /// * Contract Owner1428        /// 1429        /// # Arguments1430        /// 1431        /// -`contract_address`: Address of the contract to sponsor1432        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1433        /// 1434        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1435        pub fn set_contract_sponsoring_rate_limit(1436            origin,1437            contract_address: T::AccountId,1438            rate_limit: T::BlockNumber1439        ) -> DispatchResult {1440            let sender = ensure_signed(origin)?;14411442            #[cfg(feature = "runtime-benchmarks")]1443            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14441445            Self::ensure_contract_owned(sender, &contract_address)?;1446            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1447            Ok(())1448        }14491450        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1451        /// 1452        /// # Permissions1453        /// 1454        /// * Address that deployed smart contract.1455        /// 1456        /// # Arguments1457        /// 1458        /// -`contract_address`: Address of the contract.1459        /// 1460        /// - `enable`: .  1461        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1462        pub fn toggle_contract_white_list(1463            origin,1464            contract_address: T::AccountId,1465            enable: bool1466        ) -> DispatchResult {1467            let sender = ensure_signed(origin)?;14681469            #[cfg(feature = "runtime-benchmarks")]1470            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14711472            Self::ensure_contract_owned(sender, &contract_address)?;1473            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1474            Ok(())1475        }1476        1477        /// Add an address to smart contract white list.1478        /// 1479        /// # Permissions1480        /// 1481        /// * Address that deployed smart contract.1482        /// 1483        /// # Arguments1484        /// 1485        /// -`contract_address`: Address of the contract.1486        ///1487        /// -`account_address`: Address to add.1488        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1489        pub fn add_to_contract_white_list(1490            origin,1491            contract_address: T::AccountId,1492            account_address: T::AccountId1493        ) -> DispatchResult {1494            let sender = ensure_signed(origin)?;14951496            #[cfg(feature = "runtime-benchmarks")]1497            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1498            1499            Self::ensure_contract_owned(sender, &contract_address)?;      1500            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1501            Ok(())1502        }15031504        /// Remove an address from smart contract white list.1505        /// 1506        /// # Permissions1507        /// 1508        /// * Address that deployed smart contract.1509        /// 1510        /// # Arguments1511        /// 1512        /// -`contract_address`: Address of the contract.1513        ///1514        /// -`account_address`: Address to remove.1515        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1516        pub fn remove_from_contract_white_list(1517            origin,1518            contract_address: T::AccountId,1519            account_address: T::AccountId1520        ) -> DispatchResult {1521            let sender = ensure_signed(origin)?;15221523            #[cfg(feature = "runtime-benchmarks")]1524            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15251526            Self::ensure_contract_owned(sender, &contract_address)?;1527            <ContractWhiteList<T>>::remove(contract_address, account_address);1528            Ok(())1529        }15301531        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1532        pub fn set_collection_limits(1533            origin,1534            collection_id: u32,1535            limits: CollectionLimits,1536        ) -> DispatchResult {1537            let sender = ensure_signed(origin)?;1538            Self::check_owner_permissions(collection_id, sender.clone())?;1539            let mut target_collection = <Collection<T>>::get(collection_id);1540            let chain_limits = ChainLimit::get();1541            let climits = target_collection.limits;15421543            // collection bounds1544            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1545                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1546                Error::<T>::CollectionLimitBoundsExceeded);15471548            // token_limit   check  prev1549            ensure!(climits.token_limit > limits.token_limit && 1550                limits.token_limit <= chain_limits.account_token_ownership_limit, 1551                Error::<T>::AccountTokenLimitExceeded);15521553            ensure!(1554                (climits.owner_can_transfer || !limits.owner_can_transfer) &&1555                (climits.owner_can_destroy || !limits.owner_can_destroy),1556                Error::<T>::OwnerPermissionsCantBeReturned,1557            );15581559            target_collection.limits = limits;1560            <Collection<T>>::insert(collection_id, target_collection);15611562            Ok(())1563        } 1564    }1565}15661567impl<T: Config> Module<T> {15681569    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {15701571        let target_collection = <Collection<T>>::get(collection_id);15721573        // Limits check1574        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;15751576        // Transfer permissions check1577        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1578            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1579            Error::<T>::NoPermission);15801581        if target_collection.access == AccessMode::WhiteList {1582            Self::check_white_list(collection_id, &sender)?;1583            Self::check_white_list(collection_id, &recipient)?;1584        }15851586        match target_collection.mode1587        {1588            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1589            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1590            CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1591            _ => ()1592        };15931594        Ok(())1595    }159615971598    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15991600        // check token limit and account token limit1601        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1602        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1603        1604        Ok(())1605    }16061607    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {16081609        // check token limit and account token limit1610        let total_items: u32 = ItemListIndex::get(collection_id);1611        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1612        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1613        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);16141615        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1616            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1617            Self::check_white_list(collection_id, owner)?;1618            Self::check_white_list(collection_id, sender)?;1619        }16201621        Ok(())1622    }16231624    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1625        match target_collection.mode1626        {1627            CollectionMode::NFT => {1628                if let CreateItemData::NFT(data) = data {1629                    // check sizes1630                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1631                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1632                } else {1633                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1634                }1635            },1636            CollectionMode::Fungible(_) => {1637                if let CreateItemData::Fungible(_) = data {1638                } else {1639                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1640                }1641            },1642            CollectionMode::ReFungible => {1643                if let CreateItemData::ReFungible(data) = data {16441645                    // check sizes1646                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1647                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16481649                    // Check refungibility limits1650                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1651                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1652                } else {1653                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1654                }1655            },1656            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1657        };16581659        Ok(())1660    }16611662    fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1663        match data1664        {1665            CreateItemData::NFT(data) => {1666                let item = NftItemType {1667                    owner,1668                    const_data: data.const_data,1669                    variable_data: data.variable_data1670                };16711672                Self::add_nft_item(collection_id, item)?;1673            },1674            CreateItemData::Fungible(data) => {1675                Self::add_fungible_item(collection_id, &owner, data.value)?;1676            },1677            CreateItemData::ReFungible(data) => {1678                let mut owner_list = Vec::new();1679                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});16801681                let item = ReFungibleItemType {1682                    owner: owner_list,1683                    const_data: data.const_data,1684                    variable_data: data.variable_data1685                };16861687                Self::add_refungible_item(collection_id, item)?;1688            }1689        };16901691        // call event1692        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16931694        Ok(())1695    }16961697    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {16981699        // Does new owner already have an account?1700        let mut balance: u128 = 0;1701        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1702            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1703        } 17041705        // Mint 1706        let item = FungibleItemType {1707            value: balance + value1708        };1709        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);17101711        // Update balance1712        let new_balance = <Balance<T>>::get(collection_id, owner)1713            .checked_add(value)1714            .ok_or(Error::<T>::NumOverflow)?;1715        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17161717        Ok(())1718    }17191720    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1721        let current_index = <ItemListIndex>::get(collection_id)1722            .checked_add(1)1723            .ok_or(Error::<T>::NumOverflow)?;1724        let itemcopy = item.clone();17251726        let value = item.owner.first().unwrap().fraction;1727        let owner = item.owner.first().unwrap().owner.clone();17281729        Self::add_token_index(collection_id, current_index, &owner)?;17301731        <ItemListIndex>::insert(collection_id, current_index);1732        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17331734        // Update balance1735        let new_balance = <Balance<T>>::get(collection_id, &owner)1736            .checked_add(value)1737            .ok_or(Error::<T>::NumOverflow)?;1738        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17391740        Ok(())1741    }17421743    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1744        let current_index = <ItemListIndex>::get(collection_id)1745            .checked_add(1)1746            .ok_or(Error::<T>::NumOverflow)?;17471748        let item_owner = item.owner.clone();1749        Self::add_token_index(collection_id, current_index, &item.owner)?;17501751        <ItemListIndex>::insert(collection_id, current_index);1752        <NftItemList<T>>::insert(collection_id, current_index, item);17531754        // Update balance1755        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1756            .checked_add(1)1757            .ok_or(Error::<T>::NumOverflow)?;1758        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17591760        Ok(())1761    }17621763    fn burn_refungible_item(1764        collection_id: CollectionId,1765        item_id: TokenId,1766        owner: &T::AccountId,1767    ) -> DispatchResult {1768        ensure!(1769            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1770            Error::<T>::TokenNotFound1771        );1772        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1773        let rft_balance = token1774            .owner1775            .iter()1776            .filter(|&i| i.owner == *owner)1777            .next()1778            .unwrap();1779        Self::remove_token_index(collection_id, item_id, owner)?;17801781        // update balance1782        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1783            .checked_sub(rft_balance.fraction)1784            .ok_or(Error::<T>::NumOverflow)?;1785        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);17861787        // Re-create owners list with sender removed1788        let index = token1789            .owner1790            .iter()1791            .position(|i| i.owner == *owner)1792            .unwrap();1793        token.owner.remove(index);1794        let owner_count = token.owner.len();17951796        // Burn the token completely if this was the last (only) owner1797        if owner_count == 0 {1798            <ReFungibleItemList<T>>::remove(collection_id, item_id);1799        }1800        else {1801            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1802        }18031804        Ok(())1805    }18061807    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1808        ensure!(1809            <NftItemList<T>>::contains_key(collection_id, item_id),1810            Error::<T>::TokenNotFound1811        );1812        let item = <NftItemList<T>>::get(collection_id, item_id);1813        Self::remove_token_index(collection_id, item_id, &item.owner)?;18141815        // update balance1816        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1817            .checked_sub(1)1818            .ok_or(Error::<T>::NumOverflow)?;1819        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1820        <NftItemList<T>>::remove(collection_id, item_id);18211822        Ok(())1823    }18241825    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1826        ensure!(1827            <FungibleItemList<T>>::contains_key(collection_id, owner),1828            Error::<T>::TokenNotFound1829        );1830        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1831        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18321833        // update balance1834        let new_balance = <Balance<T>>::get(collection_id, owner)1835            .checked_sub(value)1836            .ok_or(Error::<T>::NumOverflow)?;1837        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18381839        if balance.value - value > 0 {1840            balance.value -= value;1841            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1842        }1843        else {1844            <FungibleItemList<T>>::remove(collection_id, owner);1845        }18461847        Ok(())1848    }18491850    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1851        ensure!(1852            <Collection<T>>::contains_key(collection_id),1853            Error::<T>::CollectionNotFound1854        );1855        Ok(())1856    }18571858    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1859        Self::collection_exists(collection_id)?;18601861        let target_collection = <Collection<T>>::get(collection_id);1862        ensure!(1863            subject == target_collection.owner,1864            Error::<T>::NoPermission1865        );18661867        Ok(())1868    }18691870    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1871        let target_collection = <Collection<T>>::get(collection_id);1872        let mut result: bool = subject == target_collection.owner;1873        let exists = <AdminList<T>>::contains_key(collection_id);18741875        if !result & exists {1876            if <AdminList<T>>::get(collection_id).contains(&subject) {1877                result = true1878            }1879        }18801881        result1882    }18831884    fn check_owner_or_admin_permissions(1885        collection_id: CollectionId,1886        subject: T::AccountId,1887    ) -> DispatchResult {1888        Self::collection_exists(collection_id)?;1889        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18901891        ensure!(1892            result,1893            Error::<T>::NoPermission1894        );1895        Ok(())1896    }18971898    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1899        let target_collection = <Collection<T>>::get(collection_id);19001901        match target_collection.mode {1902            CollectionMode::NFT => {1903                <NftItemList<T>>::get(collection_id, item_id).owner == subject1904            }1905            CollectionMode::Fungible(_) => {1906                <FungibleItemList<T>>::contains_key(collection_id, &subject)1907            }1908            CollectionMode::ReFungible => {1909                <ReFungibleItemList<T>>::get(collection_id, item_id)1910                    .owner1911                    .iter()1912                    .any(|i| i.owner == subject)1913            }1914            CollectionMode::Invalid => false,1915        }1916    }19171918    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1919        let mes = Error::<T>::AddresNotInWhiteList;1920        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);19211922        Ok(())1923    }19241925    /// Check if token exists. In case of Fungible, check if there is an entry for 1926    /// the owner in fungible balances double map1927    fn token_exists(1928        collection_id: CollectionId,1929        item_id: TokenId,1930        owner: &T::AccountId1931    ) -> DispatchResult {1932        let target_collection = <Collection<T>>::get(collection_id);1933        let exists = match target_collection.mode1934        {1935            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1936            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),1937            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1938            _ => false1939        };19401941        ensure!(exists == true, Error::<T>::TokenNotFound);1942        Ok(())1943    }19441945    fn transfer_fungible(1946        collection_id: CollectionId,1947        value: u128,1948        owner: &T::AccountId,1949        recipient: &T::AccountId,1950    ) -> DispatchResult {1951        Self::token_exists(collection_id, 0, owner)?;19521953        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1954        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19551956        // Send balance to recipient (updates balanceOf of recipient)1957        Self::add_fungible_item(collection_id, recipient, value)?;19581959        // update balanceOf of sender1960        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);19611962        // Reduce or remove sender1963        if balance.value == value {1964            <FungibleItemList<T>>::remove(collection_id, owner);1965        }1966        else {1967            balance.value -= value;1968            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1969        }19701971        Ok(())1972    }19731974    fn transfer_refungible(1975        collection_id: CollectionId,1976        item_id: TokenId,1977        value: u128,1978        owner: T::AccountId,1979        new_owner: T::AccountId,1980    ) -> DispatchResult {1981        Self::token_exists(collection_id, item_id, &owner)?;19821983        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1984        let item = full_item1985            .owner1986            .iter()1987            .filter(|i| i.owner == owner)1988            .next()1989            .ok_or(Error::<T>::NumOverflow)?;1990        let amount = item.fraction;19911992        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19931994        // update balance1995        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1996            .checked_sub(value)1997            .ok_or(Error::<T>::NumOverflow)?;1998        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19992000        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2001            .checked_add(value)2002            .ok_or(Error::<T>::NumOverflow)?;2003        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20042005        let old_owner = item.owner.clone();2006        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20072008        // transfer2009        if amount == value && !new_owner_has_account {2010            // change owner2011            // new owner do not have account2012            let mut new_full_item = full_item.clone();2013            new_full_item2014                .owner2015                .iter_mut()2016                .find(|i| i.owner == owner)2017                .unwrap()2018                .owner = new_owner.clone();2019            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20202021            // update index collection2022            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2023        } else {2024            let mut new_full_item = full_item.clone();2025            new_full_item2026                .owner2027                .iter_mut()2028                .find(|i| i.owner == owner)2029                .unwrap()2030                .fraction -= value;20312032            // separate amount2033            if new_owner_has_account {2034                // new owner has account2035                new_full_item2036                    .owner2037                    .iter_mut()2038                    .find(|i| i.owner == new_owner)2039                    .unwrap()2040                    .fraction += value;2041            } else {2042                // new owner do not have account2043                new_full_item.owner.push(Ownership {2044                    owner: new_owner.clone(),2045                    fraction: value,2046                });2047                Self::add_token_index(collection_id, item_id, &new_owner)?;2048            }20492050            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2051        }20522053        Ok(())2054    }20552056    fn transfer_nft(2057        collection_id: CollectionId,2058        item_id: TokenId,2059        sender: T::AccountId,2060        new_owner: T::AccountId,2061    ) -> DispatchResult {2062        Self::token_exists(collection_id, item_id, &sender)?;20632064        let mut item = <NftItemList<T>>::get(collection_id, item_id);20652066        ensure!(2067            sender == item.owner,2068            Error::<T>::MustBeTokenOwner2069        );20702071        // update balance2072        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2073            .checked_sub(1)2074            .ok_or(Error::<T>::NumOverflow)?;2075        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20762077        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2078            .checked_add(1)2079            .ok_or(Error::<T>::NumOverflow)?;2080        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20812082        // change owner2083        let old_owner = item.owner.clone();2084        item.owner = new_owner.clone();2085        <NftItemList<T>>::insert(collection_id, item_id, item);20862087        // update index collection2088        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;20892090        Ok(())2091    }2092    2093    fn set_re_fungible_variable_data(2094        collection_id: CollectionId,2095        item_id: TokenId,2096        data: Vec<u8>2097    ) -> DispatchResult {2098        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20992100        item.variable_data = data;21012102        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21032104        Ok(())2105    }21062107    fn set_nft_variable_data(2108        collection_id: CollectionId,2109        item_id: TokenId,2110        data: Vec<u8>2111    ) -> DispatchResult {2112        let mut item = <NftItemList<T>>::get(collection_id, item_id);2113        2114        item.variable_data = data;21152116        <NftItemList<T>>::insert(collection_id, item_id, item);2117        2118        Ok(())2119    }21202121    fn init_collection(item: &CollectionType<T::AccountId>) {2122        // check params2123        assert!(2124            item.decimal_points <= MAX_DECIMAL_POINTS,2125            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2126        );2127        assert!(2128            item.name.len() <= 64,2129            "Collection name can not be longer than 63 char"2130        );2131        assert!(2132            item.name.len() <= 256,2133            "Collection description can not be longer than 255 char"2134        );2135        assert!(2136            item.token_prefix.len() <= 16,2137            "Token prefix can not be longer than 15 char"2138        );21392140        // Generate next collection ID2141        let next_id = CreatedCollectionCount::get()2142            .checked_add(1)2143            .unwrap();21442145        CreatedCollectionCount::put(next_id);2146    }21472148    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2149        let current_index = <ItemListIndex>::get(collection_id)2150            .checked_add(1)2151            .unwrap();21522153        let item_owner = item.owner.clone();2154        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21552156        <ItemListIndex>::insert(collection_id, current_index);21572158        // Update balance2159        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2160            .checked_add(1)2161            .unwrap();2162        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2163    }21642165    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2166        let current_index = <ItemListIndex>::get(collection_id)2167            .checked_add(1)2168            .unwrap();21692170        Self::add_token_index(collection_id, current_index, owner).unwrap();21712172        <ItemListIndex>::insert(collection_id, current_index);21732174        // Update balance2175        let new_balance = <Balance<T>>::get(collection_id, owner)2176            .checked_add(item.value)2177            .unwrap();2178        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2179    }21802181    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2182        let current_index = <ItemListIndex>::get(collection_id)2183            .checked_add(1)2184            .unwrap();21852186        let value = item.owner.first().unwrap().fraction;2187        let owner = item.owner.first().unwrap().owner.clone();21882189        Self::add_token_index(collection_id, current_index, &owner).unwrap();21902191        <ItemListIndex>::insert(collection_id, current_index);21922193        // Update balance2194        let new_balance = <Balance<T>>::get(collection_id, &owner)2195            .checked_add(value)2196            .unwrap();2197        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2198    }21992200    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {22012202        // add to account limit2203        if <AccountItemCount<T>>::contains_key(owner) {22042205            // bound Owned tokens by a single address2206            let count = <AccountItemCount<T>>::get(owner);2207            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);22082209            <AccountItemCount<T>>::insert(owner.clone(), count2210                .checked_add(1)2211                .ok_or(Error::<T>::NumOverflow)?);2212        }2213        else {2214            <AccountItemCount<T>>::insert(owner.clone(), 1);2215        }22162217        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2218        if list_exists {2219            let mut list = <AddressTokens<T>>::get(collection_id, owner);2220            let item_contains = list.contains(&item_index.clone());22212222            if !item_contains {2223                list.push(item_index.clone());2224            }22252226            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2227        } else {2228            let mut itm = Vec::new();2229            itm.push(item_index.clone());2230            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2231        }22322233        Ok(())2234    }22352236    fn remove_token_index(2237        collection_id: CollectionId,2238        item_index: TokenId,2239        owner: &T::AccountId,2240    ) -> DispatchResult {22412242        // update counter2243        <AccountItemCount<T>>::insert(owner.clone(), 2244            <AccountItemCount<T>>::get(owner)2245            .checked_sub(1)2246            .ok_or(Error::<T>::NumOverflow)?);224722482249        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2250        if list_exists {2251            let mut list = <AddressTokens<T>>::get(collection_id, owner);2252            let item_contains = list.contains(&item_index.clone());22532254            if item_contains {2255                list.retain(|&item| item != item_index);2256                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2257            }2258        }22592260        Ok(())2261    }22622263    fn move_token_index(2264        collection_id: CollectionId,2265        item_index: TokenId,2266        old_owner: &T::AccountId,2267        new_owner: &T::AccountId,2268    ) -> DispatchResult {2269        Self::remove_token_index(collection_id, item_index, old_owner)?;2270        Self::add_token_index(collection_id, item_index, new_owner)?;22712272        Ok(())2273    }2274    2275    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2276        if <ContractOwner<T>>::contains_key(contract.clone()) {2277            let owner = <ContractOwner<T>>::get(contract);2278            ensure!(account == owner, Error::<T>::NoPermission);2279        } else {2280            fail!(Error::<T>::NoPermission);2281        }22822283        Ok(())2284    }2285}22862287////////////////////////////////////////////////////////////////////////////////////////////////////2288// Economic models2289// #region22902291/// Fee multiplier.2292pub type Multiplier = FixedU128;22932294type BalanceOf<T> = <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::Balance;22952296/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2297/// in the queue.2298#[derive(Encode, Decode, Clone, Eq, PartialEq)]2299pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);23002301impl<T: Config + Send + Sync> sp_std::fmt::Debug 2302    for ChargeTransactionPayment<T>2303{2304	#[cfg(feature = "std")]2305	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2306		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2307	}2308	#[cfg(not(feature = "std"))]2309	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2310		Ok(())2311	}2312}23132314impl<T: Config> ChargeTransactionPayment<T>2315where2316    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2317    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2318    T::AccountId: AsRef<[u8]>,2319    T::AccountId: UncheckedFrom<T::Hash>,2320{2321    fn traditional_fee(2322        len: usize,2323        info: &DispatchInfoOf<T::Call>,2324        tip: BalanceOf<T>,2325    ) -> BalanceOf<T>2326    where2327        T::Call: Dispatchable<Info = DispatchInfo>,2328    {2329        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2330    }23312332	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2333        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2334        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2335        let len_saturation = max_block_length as u64 / (len as u64).max(1);2336        let coefficient: BalanceOf<T> = weight_saturation2337            .min(len_saturation)2338            .saturated_into::<BalanceOf<T>>();2339        final_fee2340            .saturating_mul(coefficient)2341            .saturated_into::<TransactionPriority>()2342    }23432344    fn withdraw_fee(2345        &self,2346        who: &T::AccountId,2347        call: &T::Call,2348        info: &DispatchInfoOf<T::Call>,2349        len: usize,2350	) -> Result<2351		(2352			BalanceOf<T>,2353			<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2354		),2355		TransactionValidityError,2356	> {2357        let tip = self.0;23582359        // Set fee based on call type. Creating collection costs 1 Unique.2360        // All other transactions have traditional fees so far2361        // let fee = match call.is_sub_type() {2362        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2363        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2364        //                                                 // _ => <BalanceOf<T>>::from(100)2365        // };2366        let fee = Self::traditional_fee(len, info, tip);23672368        // Only mess with balances if fee is not zero.2369        if fee.is_zero() {2370            return <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2371			.map(|i| (fee, i));2372        }23732374        // Determine who is paying transaction fee based on ecnomic model2375        // Parse call to extract collection ID and access collection sponsor2376        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2377            Some(Call::create_item(collection_id, _owner, _properties)) => {23782379                // sponsor timeout2380                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;23812382                let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;2383                let mut sponsored = true;2384                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2385                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2386                    let limit_time = last_tx_block + limit.into();2387                    if block_number <= limit_time {2388                        sponsored = false;2389                    }2390                }2391                if sponsored {2392                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2393                }23942395                // check free create limit2396                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2397                   (<Collection<T>>::get(collection_id).sponsor_confirmed) &&2398                   (sponsored)2399                {2400                    <Collection<T>>::get(collection_id).sponsor2401                } else {2402                    T::AccountId::default()2403                }2404            }2405            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2406                2407                let mut sponsor_transfer = false;2408                if <Collection<T>>::get(collection_id).sponsor_confirmed {24092410                    let collection_limits = <Collection<T>>::get(collection_id).limits;2411                    let collection_mode = <Collection<T>>::get(collection_id).mode;2412    2413                    // sponsor timeout2414                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2415                    sponsor_transfer = match collection_mode {2416                        CollectionMode::NFT => {2417    2418                            // get correct limit2419                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2420                                collection_limits.sponsor_transfer_timeout2421                            } else {2422                                ChainLimit::get().nft_sponsor_transfer_timeout2423                            };2424    2425                            let mut sponsored = true;2426                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2427                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2428                                let limit_time = last_tx_block + limit.into();2429                                if block_number <= limit_time {2430                                    sponsored = false;2431                                }2432                            }2433                            if sponsored {2434                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2435                            }24362437                            sponsored2438                        }2439                        CollectionMode::Fungible(_) => {2440    2441                            // get correct limit2442                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2443                                collection_limits.sponsor_transfer_timeout2444                            } else {2445                                ChainLimit::get().fungible_sponsor_transfer_timeout2446                            };2447    2448                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2449                            let mut sponsored = true;2450                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2451                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2452                                let limit_time = last_tx_block + limit.into();2453                                if block_number <= limit_time {2454                                    sponsored = false;2455                                }2456                            }2457                            if sponsored {2458                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2459                            }24602461                            sponsored2462                        }2463                        CollectionMode::ReFungible => {2464    2465                            // get correct limit2466                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2467                                collection_limits.sponsor_transfer_timeout2468                            } else {2469                                ChainLimit::get().refungible_sponsor_transfer_timeout2470                            };2471    2472                            let mut sponsored = true;2473                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2474                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2475                                let limit_time = last_tx_block + limit.into();2476                                if block_number <= limit_time {2477                                    sponsored = false;2478                                }2479                            }2480                            if sponsored {2481                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2482                            }24832484                            sponsored2485                        }2486                        _ => {2487                            false2488                        },2489                    };2490                }24912492                if !sponsor_transfer {2493                    T::AccountId::default()2494                } else {2495                    <Collection<T>>::get(collection_id).sponsor2496                }2497            }24982499            _ => T::AccountId::default(),2500        };25012502        // Sponsor smart contracts2503        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {25042505            // On instantiation: set the contract owner2506            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {25072508                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2509                    &who,2510                    code_hash,2511                    salt,2512                );2513                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());25142515                T::AccountId::default()2516            },25172518            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2519            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {25202521                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());25222523                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2524                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2525                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2526                  2527                if !owned_contract && white_list_enabled {2528                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2529                        return Err(InvalidTransaction::Call.into());2530                    }2531                }25322533                let mut sponsor_transfer = false;2534                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2535                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2536                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2537                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2538                    let limit_time = last_tx_block + rate_limit;25392540                    if block_number >= limit_time {2541                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2542                        sponsor_transfer = true;2543                    }2544                } else {2545                    sponsor_transfer = false;2546                }2547               2548                2549                let mut sp = T::AccountId::default();2550                if sponsor_transfer {2551                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2552                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2553                            sp = called_contract;2554                        }2555                    }2556                }25572558                sp2559            },25602561            _ => sponsor,2562        };25632564        let mut who_pays_fee: T::AccountId = sponsor.clone();2565        if sponsor == T::AccountId::default() {2566            who_pays_fee = who.clone();2567        }25682569		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2570			.map(|i| (fee, i))2571    }2572}257325742575impl<T: Config + Send + Sync> SignedExtension2576    for ChargeTransactionPayment<T>2577where2578    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2579    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2580    T::AccountId: AsRef<[u8]>,2581    T::AccountId: UncheckedFrom<T::Hash>,2582{2583    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2584    type AccountId = T::AccountId;2585    type Call = T::Call;2586    type AdditionalSigned = ();2587    type Pre = (2588        // tip2589        BalanceOf<T>,2590        // who pays fee2591        Self::AccountId,2592		// imbalance resulting from withdrawing the fee2593		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2594    );2595    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2596        Ok(())2597    }25982599    fn validate(2600        &self,2601        who: &Self::AccountId,2602        call: &Self::Call,2603        info: &DispatchInfoOf<Self::Call>,2604        len: usize,2605    ) -> TransactionValidity {2606		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2607		Ok(ValidTransaction {2608			priority: Self::get_priority(len, info, fee),2609			..Default::default()2610		})2611    }26122613    fn pre_dispatch(2614        self,2615        who: &Self::AccountId,2616        call: &Self::Call,2617        info: &DispatchInfoOf<Self::Call>,2618        len: usize,2619    ) -> Result<Self::Pre, TransactionValidityError> {2620        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2621        Ok((self.0, who.clone(), imbalance))2622    }26232624    fn post_dispatch(2625        pre: Self::Pre,2626        info: &DispatchInfoOf<Self::Call>,2627        post_info: &PostDispatchInfoOf<Self::Call>,2628        len: usize,2629        _result: &DispatchResult,2630    ) -> Result<(), TransactionValidityError> {2631		let (tip, who, imbalance) = pre;2632		let actual_fee = transaction_payment::Module::<T>::compute_actual_fee(2633			len as u32,2634			info,2635			post_info,2636			tip,2637		);2638		<T as transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2639		Ok(())2640    }2641}26422643// #endregion