git.delta.rocks / unique-network / refs/commits / 2a441390261c

difftreelog

source

pallets/nft/src/lib.rs82.8 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::*;1516pub use frame_support::{17    construct_runtime, decl_event, decl_module, decl_storage, decl_error,18    dispatch::DispatchResult,19    ensure, fail, parameter_types,20    traits::{21        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,22        Randomness, IsSubType, WithdrawReasons,23    },24    weights::{25        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},26        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,27        WeightToFeePolynomial, DispatchClass,28    },29    StorageValue,30    transactional,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use core::ops::{Deref, DerefMut};36use nft_data_structs::{37    MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,38	AccessMode, ChainLimits, Collection, CreateItemData, CollectionLimits,39    CollectionId, CollectionMode, TokenId, 40    SchemaVersion, SponsorshipState, Ownership,41    NftItemType, FungibleItemType, ReFungibleItemType42};4344#[cfg(test)]45mod mock;4647#[cfg(test)]48mod tests;4950mod default_weights;5152#[cfg(feature = "runtime-benchmarks")]53mod benchmarking;5455pub trait WeightInfo {56	fn create_collection() -> Weight;57	fn destroy_collection() -> Weight;58	fn add_to_white_list() -> Weight;59	fn remove_from_white_list() -> Weight;60    fn set_public_access_mode() -> Weight;61    fn set_mint_permission() -> Weight;62    fn change_collection_owner() -> Weight;63    fn add_collection_admin() -> Weight;64    fn remove_collection_admin() -> Weight;65    fn set_collection_sponsor() -> Weight;66    fn confirm_sponsorship() -> Weight;67    fn remove_collection_sponsor() -> Weight;68    fn create_item(s: usize) -> Weight;69    fn burn_item() -> Weight;70    fn transfer() -> Weight;71    fn approve() -> Weight;72    fn transfer_from() -> Weight;73    fn set_offchain_schema() -> Weight;74    fn set_const_on_chain_schema() -> Weight;75    fn set_variable_on_chain_schema() -> Weight;76    fn set_variable_meta_data() -> Weight;77    fn enable_contract_sponsoring() -> Weight;78    fn set_schema_version() -> Weight;79    fn set_chain_limits() -> Weight;80    fn set_contract_sponsoring_rate_limit() -> Weight;81    fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;82    fn toggle_contract_white_list() -> Weight;83    fn add_to_contract_white_list() -> Weight;84    fn remove_from_contract_white_list() -> Weight;85    fn set_collection_limits() -> Weight;86}8788decl_error! {89	/// Error for non-fungible-token module.90	pub enum Error for Module<T: Config> {91        /// Total collections bound exceeded.92        TotalCollectionsLimitExceeded,93		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.94        CollectionDecimalPointLimitExceeded, 95        /// Collection name can not be longer than 63 char.96        CollectionNameLimitExceeded, 97        /// Collection description can not be longer than 255 char.98        CollectionDescriptionLimitExceeded, 99        /// Token prefix can not be longer than 15 char.100        CollectionTokenPrefixLimitExceeded,101        /// This collection does not exist.102        CollectionNotFound,103        /// Item not exists.104        TokenNotFound,105        /// Admin not found106        AdminNotFound,107        /// Arithmetic calculation overflow.108        NumOverflow,       109        /// Account already has admin role.110        AlreadyAdmin,  111        /// You do not own this collection.112        NoPermission,113        /// This address is not set as sponsor, use setCollectionSponsor first.114        ConfirmUnsetSponsorFail,115        /// Collection is not in mint mode.116        PublicMintingNotAllowed,117        /// Sender parameter and item owner must be equal.118        MustBeTokenOwner,119        /// Item balance not enough.120        TokenValueTooLow,121        /// Size of item is too large.122        NftSizeLimitExceeded,123        /// No approve found124        ApproveNotFound,125        /// Requested value more than approved.126        TokenValueNotEnough,127        /// Only approved addresses can call this method.128        ApproveRequired,129        /// Address is not in white list.130        AddresNotInWhiteList,131        /// Number of collection admins bound exceeded.132        CollectionAdminsLimitExceeded,133        /// Owned tokens by a single address bound exceeded.134        AddressOwnershipLimitExceeded,135        /// Length of items properties must be greater than 0.136        EmptyArgument,137        /// const_data exceeded data limit.138        TokenConstDataLimitExceeded,139        /// variable_data exceeded data limit.140        TokenVariableDataLimitExceeded,141        /// Not NFT item data used to mint in NFT collection.142        NotNftDataUsedToMintNftCollectionToken,143        /// Not Fungible item data used to mint in Fungible collection.144        NotFungibleDataUsedToMintFungibleCollectionToken,145        /// Not Re Fungible item data used to mint in Re Fungible collection.146        NotReFungibleDataUsedToMintReFungibleCollectionToken,147        /// Unexpected collection type.148        UnexpectedCollectionType,149        /// Can't store metadata in fungible tokens.150        CantStoreMetadataInFungibleTokens,151        /// Collection token limit exceeded152        CollectionTokenLimitExceeded,153        /// Account token limit exceeded per collection154        AccountTokenLimitExceeded,155        /// Collection limit bounds per collection exceeded156        CollectionLimitBoundsExceeded,157        /// Tried to enable permissions which are only permitted to be disabled158        OwnerPermissionsCantBeReverted,159        /// Schema data size limit bound exceeded160        SchemaDataLimitExceeded,161        /// Maximum refungibility exceeded162        WrongRefungiblePieces,163        /// createRefungible should be called with one owner164        BadCreateRefungibleCall,165	}166}167168pub struct CollectionHandle<T: frame_system::Config> {169    pub id: CollectionId,170    pub collection: Collection<T>,171}172173impl<T: frame_system::Config> Deref for CollectionHandle<T> {174    type Target = Collection<T>;175176    fn deref(&self) -> &Self::Target {177        &self.collection178    }179}180181impl<T: frame_system::Config> DerefMut for CollectionHandle<T> {182    fn deref_mut(&mut self) -> &mut Self::Target {183        &mut self.collection184    }185}186187188189pub trait Config: system::Config + Sized {190    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;191192    /// Weight information for extrinsics in this pallet.193	type WeightInfo: WeightInfo;194195    type Currency: Currency<Self::AccountId>;196    type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;197    type TreasuryAccountId: Get<Self::AccountId>;198}199200// # Used definitions201//202// ## User control levels203//204// chain-controlled - key is uncontrolled by user205//                    i.e autoincrementing index206//                    can use non-cryptographic hash207// real - key is controlled by user208//        but it is hard to generate enough colliding values, i.e owner of signed txs209//        can use non-cryptographic hash210// controlled - key is completly controlled by users211//              i.e maps with mutable keys212//              should use cryptographic hash213//214// ## User control level downgrade reasons215//216// ?1 - chain-controlled -> controlled217//      collections/tokens can be destroyed, resulting in massive holes218// ?2 - chain-controlled -> controlled219//      same as ?1, but can be only added, resulting in easier exploitation220// ?3 - real -> controlled221//      no confirmation required, so addresses can be easily generated222decl_storage! {223    trait Store for Module<T: Config> as Nft {224225        //#region Private members226        /// Id of next collection227        CreatedCollectionCount: u32;228        /// Used for migrations229        ChainVersion: u64;230        /// Id of last collection token231        /// Collection id (controlled?1)232        ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;233        //#endregion234235        //#region Chain limits struct236        pub ChainLimit get(fn chain_limit) config(): ChainLimits;237        //#endregion238239        //#region Bound counters240        /// Amount of collections destroyed, used for total amount tracking with241        /// CreatedCollectionCount242        DestroyedCollectionCount: u32;243        /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)244        /// Account id (real)245        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;246        //#endregion247248        //#region Basic collections249        /// Collection info250        /// Collection id (controlled?1)251        pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;252        /// List of collection admins253        /// Collection id (controlled?2)254        pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::AccountId>;255        /// Whitelisted collection users256        /// Collection id (controlled?2), user id (controlled?3)257        pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;258        //#endregion259260        /// How many of collection items user have261        /// Collection id (controlled?2), account id (real)262        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;263264        /// Amount of items which spender can transfer out of owners account (via transferFrom)265        /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))266        /// TODO: Off chain worker should remove from this map when token gets removed267        pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;268269        //#region Item collections270        /// Collection id (controlled?2), token id (controlled?1)271        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::AccountId>>;272        /// Collection id (controlled?2), owner (controlled?2)273        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;274        /// Collection id (controlled?2), token id (controlled?1)275        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::AccountId>>;276        //#endregion277278        //#region Index list279        /// Collection id (controlled?2), tokens owner (controlled?2)280        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;281        //#endregion282283        //#region Tokens transfer rate limit baskets284        /// (Collection id (controlled?2), who created (real))285        /// TODO: Off chain worker should remove from this map when collection gets removed286        pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;287        /// Collection id (controlled?2), token id (controlled?2)288        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;289        /// Collection id (controlled?2), owning user (real)290        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;291        /// Collection id (controlled?2), token id (controlled?2)292        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;293        //#endregion294295        /// Variable metadata sponsoring296        /// Collection id (controlled?2), token id (controlled?2)297        pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;298      299        //#region Contract Sponsorship and Ownership300        /// Contract address (real)301        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;302        /// Contract address (real)303        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;304        /// (Contract address(real), caller (real))305        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;306        /// Contract address (real)307        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;308        /// Contract address (real)309        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 310        /// Contract address (real) => Whitelisted user (controlled?3)311        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 312        //#endregion313    }314    add_extra_genesis {315        build(|config: &GenesisConfig<T>| {316            // Modification of storage317            for (_num, _c) in &config.collection_id {318                <Module<T>>::init_collection(_c);319            }320321            for (_num, _c, _i) in &config.nft_item_id {322                <Module<T>>::init_nft_token(*_c, _i);323            }324325            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {326                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);327            }328329            for (_num, _c, _i) in &config.refungible_item_id {330                <Module<T>>::init_refungible_token(*_c, _i);331            }332        })333    }334}335336decl_event!(337    pub enum Event<T>338    where339        AccountId = <T as system::Config>::AccountId,340    {341        /// New collection was created342        /// 343        /// # Arguments344        /// 345        /// * collection_id: Globally unique identifier of newly created collection.346        /// 347        /// * mode: [CollectionMode] converted into u8.348        /// 349        /// * account_id: Collection owner.350        CollectionCreated(CollectionId, u8, AccountId),351352        /// New item was created.353        /// 354        /// # Arguments355        /// 356        /// * collection_id: Id of the collection where item was created.357        /// 358        /// * item_id: Id of an item. Unique within the collection.359        ///360        /// * recipient: Owner of newly created item 361        ItemCreated(CollectionId, TokenId, AccountId),362363        /// Collection item was burned.364        /// 365        /// # Arguments366        /// 367        /// collection_id.368        /// 369        /// item_id: Identifier of burned NFT.370        ItemDestroyed(CollectionId, TokenId),371372        /// Item was transferred373        ///374        /// * collection_id: Id of collection to which item is belong375        ///376        /// * item_id: Id of an item377        ///378        /// * sender: Original owner of item379        ///380        /// * recipient: New owner of item381        ///382        /// * amount: Always 1 for NFT383        Transfer(CollectionId, TokenId, AccountId, AccountId, u128),384385        /// * collection_id386        ///387        /// * item_id388        ///389        /// * sender390        ///391        /// * spender392        ///393        /// * amount394        Approved(CollectionId, TokenId, AccountId, AccountId, u128),395    }396);397398decl_module! {399    pub struct Module<T: Config> for enum Call 400    where 401        origin: T::Origin402    {403        fn deposit_event() = default;404        type Error = Error<T>;405406        fn on_initialize(_now: T::BlockNumber) -> Weight {407            0408        }409410        /// 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.411        /// 412        /// # Permissions413        /// 414        /// * Anyone.415        /// 416        /// # Arguments417        /// 418        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.419        /// 420        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.421        /// 422        /// * token_prefix: UTF-8 string with token prefix.423        /// 424        /// * mode: [CollectionMode] collection type and type dependent data.425        // returns collection ID426        #[weight = <T as Config>::WeightInfo::create_collection()]427        #[transactional]428        pub fn create_collection(origin,429                                 collection_name: Vec<u16>,430                                 collection_description: Vec<u16>,431                                 token_prefix: Vec<u8>,432                                 mode: CollectionMode) -> DispatchResult {433434            // Anyone can create a collection435            let who = ensure_signed(origin)?;436437            // Take a (non-refundable) deposit of collection creation438            let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();439            imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(440                &T::TreasuryAccountId::get(),441                T::CollectionCreationPrice::get(),442            ));443            <T as Config>::Currency::settle(444                &who,445                imbalance,446                WithdrawReasons::TRANSFER,447                ExistenceRequirement::KeepAlive,448            ).map_err(|_| Error::<T>::NoPermission)?;449450            let decimal_points = match mode {451                CollectionMode::Fungible(points) => points,452                _ => 0453            };454455            let chain_limit = ChainLimit::get();456457            let created_count = CreatedCollectionCount::get();458            let destroyed_count = DestroyedCollectionCount::get();459460            // bound Total number of collections461            ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);462463            // check params464            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);465            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);466            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);467            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);468469            // Generate next collection ID470            let next_id = created_count471                .checked_add(1)472                .ok_or(Error::<T>::NumOverflow)?;473474            CreatedCollectionCount::put(next_id);475476            let limits = CollectionLimits {477                sponsored_data_size: chain_limit.custom_data_limit,478                ..Default::default()479            };480481            // Create new collection482            let new_collection = Collection {483                owner: who.clone(),484                name: collection_name,485                mode: mode.clone(),486                mint_mode: false,487                access: AccessMode::Normal,488                description: collection_description,489                decimal_points: decimal_points,490                token_prefix: token_prefix,491                offchain_schema: Vec::new(),492                schema_version: SchemaVersion::ImageURL,493                sponsorship: SponsorshipState::Disabled,494                variable_on_chain_schema: Vec::new(),495                const_on_chain_schema: Vec::new(),496                limits,497            };498499            // Add new collection to map500            <CollectionById<T>>::insert(next_id, new_collection);501502            // call event503            Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who.clone()));504505            Ok(())506        }507508        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.509        /// 510        /// # Permissions511        /// 512        /// * Collection Owner.513        /// 514        /// # Arguments515        /// 516        /// * collection_id: collection to destroy.517        #[weight = <T as Config>::WeightInfo::destroy_collection()]518        #[transactional]519        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {520521            let sender = ensure_signed(origin)?;522            let collection = Self::get_collection(collection_id)?;523            Self::check_owner_permissions(&collection, sender)?;524            if !collection.limits.owner_can_destroy {525                fail!(Error::<T>::NoPermission);526            }527528            <AddressTokens<T>>::remove_prefix(collection_id);529            <Allowances<T>>::remove_prefix(collection_id);530            <Balance<T>>::remove_prefix(collection_id);531            <ItemListIndex>::remove(collection_id);532            <AdminList<T>>::remove(collection_id);533            <CollectionById<T>>::remove(collection_id);534            <WhiteList<T>>::remove_prefix(collection_id);535536            <NftItemList<T>>::remove_prefix(collection_id);537            <FungibleItemList<T>>::remove_prefix(collection_id);538            <ReFungibleItemList<T>>::remove_prefix(collection_id);539540            <NftTransferBasket<T>>::remove_prefix(collection_id);541            <FungibleTransferBasket<T>>::remove_prefix(collection_id);542            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);543544            <VariableMetaDataBasket<T>>::remove_prefix(collection_id);545546            DestroyedCollectionCount::put(DestroyedCollectionCount::get()547                .checked_add(1)548                .ok_or(Error::<T>::NumOverflow)?);549550            Ok(())551        }552553        /// Add an address to white list.554        /// 555        /// # Permissions556        /// 557        /// * Collection Owner558        /// * Collection Admin559        /// 560        /// # Arguments561        /// 562        /// * collection_id.563        /// 564        /// * address.565        #[weight = <T as Config>::WeightInfo::add_to_white_list()]566        #[transactional]567        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{568569            let sender = ensure_signed(origin)?;570            let collection = Self::get_collection(collection_id)?;571            Self::check_owner_or_admin_permissions(&collection, sender)?;572573            <WhiteList<T>>::insert(collection_id, address, true);574            575            Ok(())576        }577578        /// Remove an address from white list.579        /// 580        /// # Permissions581        /// 582        /// * Collection Owner583        /// * Collection Admin584        /// 585        /// # Arguments586        /// 587        /// * collection_id.588        /// 589        /// * address.590        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]591        #[transactional]592        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{593594            let sender = ensure_signed(origin)?;595            let collection = Self::get_collection(collection_id)?;596            Self::check_owner_or_admin_permissions(&collection, sender)?;597598            <WhiteList<T>>::remove(collection_id, address);599600            Ok(())601        }602603        /// Toggle between normal and white list access for the methods with access for `Anyone`.604        /// 605        /// # Permissions606        /// 607        /// * Collection Owner.608        /// 609        /// # Arguments610        /// 611        /// * collection_id.612        /// 613        /// * mode: [AccessMode]614        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]615        #[transactional]616        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult617        {618            let sender = ensure_signed(origin)?;619620            let mut target_collection = Self::get_collection(collection_id)?;621            Self::check_owner_permissions(&target_collection, sender)?;622            target_collection.access = mode;623            Self::save_collection(target_collection);624625            Ok(())626        }627628        /// Allows Anyone to create tokens if:629        /// * White List is enabled, and630        /// * Address is added to white list, and631        /// * This method was called with True parameter632        /// 633        /// # Permissions634        /// * Collection Owner635        ///636        /// # Arguments637        /// 638        /// * collection_id.639        /// 640        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.641        #[weight = <T as Config>::WeightInfo::set_mint_permission()]642        #[transactional]643        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult644        {645            let sender = ensure_signed(origin)?;646647            let mut target_collection = Self::get_collection(collection_id)?;648            Self::check_owner_permissions(&target_collection, sender)?;649            target_collection.mint_mode = mint_permission;650            Self::save_collection(target_collection);651652            Ok(())653        }654655        /// Change the owner of the collection.656        /// 657        /// # Permissions658        /// 659        /// * Collection Owner.660        /// 661        /// # Arguments662        /// 663        /// * collection_id.664        /// 665        /// * new_owner.666        #[weight = <T as Config>::WeightInfo::change_collection_owner()]667        #[transactional]668        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {669670            let sender = ensure_signed(origin)?;671            let mut target_collection = Self::get_collection(collection_id)?;672            Self::check_owner_permissions(&target_collection, sender)?;673            target_collection.owner = new_owner;674            Self::save_collection(target_collection);675676            Ok(())677        }678679        /// Adds an admin of the Collection.680        /// 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. 681        /// 682        /// # Permissions683        /// 684        /// * Collection Owner.685        /// * Collection Admin.686        /// 687        /// # Arguments688        /// 689        /// * collection_id: ID of the Collection to add admin for.690        /// 691        /// * new_admin_id: Address of new admin to add.692        #[weight = <T as Config>::WeightInfo::add_collection_admin()]693        #[transactional]694        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {695696            let sender = ensure_signed(origin)?;697            let collection = Self::get_collection(collection_id)?;698            Self::check_owner_or_admin_permissions(&collection, sender)?;699            let mut admin_arr = <AdminList<T>>::get(collection_id);700701            match admin_arr.binary_search(&new_admin_id) {702                Ok(_) => {},703                Err(idx) => {704                    let limits = ChainLimit::get();705                    ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);706                    admin_arr.insert(idx, new_admin_id);707                    <AdminList<T>>::insert(collection_id, admin_arr);708                }709            }710            Ok(())711        }712713        /// 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.714        ///715        /// # Permissions716        /// 717        /// * Collection Owner.718        /// * Collection Admin.719        /// 720        /// # Arguments721        /// 722        /// * collection_id: ID of the Collection to remove admin for.723        /// 724        /// * account_id: Address of admin to remove.725        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]726        #[transactional]727        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {728729            let sender = ensure_signed(origin)?;730            let collection = Self::get_collection(collection_id)?;731            Self::check_owner_or_admin_permissions(&collection, sender)?;732            let mut admin_arr = <AdminList<T>>::get(collection_id);733734            match admin_arr.binary_search(&account_id) {735                Ok(idx) => {736                    admin_arr.remove(idx);737                    <AdminList<T>>::insert(collection_id, admin_arr);738                },739                Err(_) => {}740            }741            Ok(())742        }743744        /// # Permissions745        /// 746        /// * Collection Owner747        /// 748        /// # Arguments749        /// 750        /// * collection_id.751        /// 752        /// * new_sponsor.753        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]754        #[transactional]755        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {756757            let sender = ensure_signed(origin)?;758            let mut target_collection = Self::get_collection(collection_id)?;759            Self::check_owner_permissions(&target_collection, sender)?;760761            target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);762            Self::save_collection(target_collection);763764            Ok(())765        }766767        /// # Permissions768        /// 769        /// * Sponsor.770        /// 771        /// # Arguments772        /// 773        /// * collection_id.774        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]775        #[transactional]776        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {777778            let sender = ensure_signed(origin)?;779780            let mut target_collection = Self::get_collection(collection_id)?;781            ensure!(782                target_collection.sponsorship.pending_sponsor() == Some(&sender),783                Error::<T>::ConfirmUnsetSponsorFail784            );785786            target_collection.sponsorship = SponsorshipState::Confirmed(sender);787            Self::save_collection(target_collection);788789            Ok(())790        }791792        /// Switch back to pay-per-own-transaction model.793        ///794        /// # Permissions795        ///796        /// * Collection owner.797        /// 798        /// # Arguments799        /// 800        /// * collection_id.801        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]802        #[transactional]803        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {804805            let sender = ensure_signed(origin)?;806807            let mut target_collection = Self::get_collection(collection_id)?;808            Self::check_owner_permissions(&target_collection, sender)?;809810            target_collection.sponsorship = SponsorshipState::Disabled;811            Self::save_collection(target_collection);812813            Ok(())814        }815816        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.817        /// 818        /// # Permissions819        /// 820        /// * Collection Owner.821        /// * Collection Admin.822        /// * Anyone if823        ///     * White List is enabled, and824        ///     * Address is added to white list, and825        ///     * MintPermission is enabled (see SetMintPermission method)826        /// 827        /// # Arguments828        /// 829        /// * collection_id: ID of the collection.830        /// 831        /// * owner: Address, initial owner of the NFT.832        ///833        /// * data: Token data to store on chain.834        // #[weight =835        // (130_000_000 as Weight)836        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))837        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))838        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]839840        #[weight = <T as Config>::WeightInfo::create_item(data.len())]841        #[transactional]842        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {843            let sender = ensure_signed(origin)?;844            Self::create_item_internal(sender, collection_id, owner, data)845        }846847        /// This method creates multiple items in a collection created with CreateCollection method.848        /// 849        /// # Permissions850        /// 851        /// * Collection Owner.852        /// * Collection Admin.853        /// * Anyone if854        ///     * White List is enabled, and855        ///     * Address is added to white list, and856        ///     * MintPermission is enabled (see SetMintPermission method)857        /// 858        /// # Arguments859        /// 860        /// * collection_id: ID of the collection.861        /// 862        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].863        /// 864        /// * owner: Address, initial owner of the NFT.865        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()866                               .map(|data| { data.len() })867                               .sum())]868        #[transactional]869        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {870871            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);872            let sender = ensure_signed(origin)?;873874            let target_collection = Self::get_collection(collection_id)?;875876            Self::can_create_items_in_collection(&target_collection, &sender, &owner, items_data.len() as u32)?;877878            for data in &items_data {879                Self::validate_create_item_args(&target_collection, data)?;880            }881            for data in &items_data {882                Self::create_item_no_validation(&target_collection, owner.clone(), data.clone())?;883            }884885            Ok(())886        }887888        /// Destroys a concrete instance of NFT.889        /// 890        /// # Permissions891        /// 892        /// * Collection Owner.893        /// * Collection Admin.894        /// * Current NFT Owner.895        /// 896        /// # Arguments897        /// 898        /// * collection_id: ID of the collection.899        /// 900        /// * item_id: ID of NFT to burn.901        #[weight = <T as Config>::WeightInfo::burn_item()]902        #[transactional]903        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {904905            let sender = ensure_signed(origin)?;906907            // Transfer permissions check908            let target_collection = Self::get_collection(collection_id)?;909            ensure!(910                Self::is_item_owner(sender.clone(), &target_collection, item_id) ||911                (912                    target_collection.limits.owner_can_transfer &&913                    Self::is_owner_or_admin_permissions(&target_collection, sender.clone())914                ),915                Error::<T>::NoPermission916            );917918            if target_collection.access == AccessMode::WhiteList {919                Self::check_white_list(&target_collection, &sender)?;920            }921922            match target_collection.mode923            {924                CollectionMode::NFT => Self::burn_nft_item(&target_collection, item_id)?,925                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, &target_collection, value)?,926                CollectionMode::ReFungible  => Self::burn_refungible_item(&target_collection, item_id, &sender)?,927                _ => ()928            };929930            // call event931            Self::deposit_event(RawEvent::ItemDestroyed(target_collection.id, item_id));932933            Ok(())934        }935936        /// Change ownership of the token.937        /// 938        /// # Permissions939        /// 940        /// * Collection Owner941        /// * Collection Admin942        /// * Current NFT owner943        ///944        /// # Arguments945        /// 946        /// * recipient: Address of token recipient.947        /// 948        /// * collection_id.949        /// 950        /// * item_id: ID of the item951        ///     * Non-Fungible Mode: Required.952        ///     * Fungible Mode: Ignored.953        ///     * Re-Fungible Mode: Required.954        /// 955        /// * value: Amount to transfer.956        ///     * Non-Fungible Mode: Ignored957        ///     * Fungible Mode: Must specify transferred amount958        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)959        #[weight = <T as Config>::WeightInfo::transfer()]960        #[transactional]961        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {962            let sender = ensure_signed(origin)?;963            let collection = Self::get_collection(collection_id)?;964965            Self::transfer_internal(sender, recipient, &collection, item_id, value)966        }967968        /// Set, change, or remove approved address to transfer the ownership of the NFT.969        /// 970        /// # Permissions971        /// 972        /// * Collection Owner973        /// * Collection Admin974        /// * Current NFT owner975        /// 976        /// # Arguments977        /// 978        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).979        /// 980        /// * collection_id.981        /// 982        /// * item_id: ID of the item.983        #[weight = <T as Config>::WeightInfo::approve()]984        #[transactional]985        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {986987            let sender = ensure_signed(origin)?;988            let target_collection = Self::get_collection(collection_id)?;989990            Self::token_exists(&target_collection, item_id)?;991992            // Transfer permissions check993            let bypasses_limits = target_collection.limits.owner_can_transfer &&994                Self::is_owner_or_admin_permissions(995                    &target_collection,996                    sender.clone(),997                );998999            let allowance_limit = if bypasses_limits {1000                None1001            } else if let Some(amount) = Self::owned_amount(1002                sender.clone(),1003                &target_collection,1004                item_id,1005            ) {1006                Some(amount)1007            } else {1008                fail!(Error::<T>::NoPermission);1009            };10101011            if target_collection.access == AccessMode::WhiteList {1012                Self::check_white_list(&target_collection, &sender)?;1013                Self::check_white_list(&target_collection, &spender)?;1014            }10151016            let allowance: u128 = amount1017                .checked_add(<Allowances<T>>::get(collection_id, (item_id, &sender, &spender)))1018                .ok_or(Error::<T>::NumOverflow)?;1019            if let Some(limit) = allowance_limit {1020                ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1021            }1022            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);10231024            Self::deposit_event(RawEvent::Approved(target_collection.id, item_id, sender, spender, allowance));1025            Ok(())1026        }1027        1028        /// 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.1029        /// 1030        /// # Permissions1031        /// * Collection Owner1032        /// * Collection Admin1033        /// * Current NFT owner1034        /// * Address approved by current NFT owner1035        /// 1036        /// # Arguments1037        /// 1038        /// * from: Address that owns token.1039        /// 1040        /// * recipient: Address of token recipient.1041        /// 1042        /// * collection_id.1043        /// 1044        /// * item_id: ID of the item.1045        /// 1046        /// * value: Amount to transfer.1047        #[weight = <T as Config>::WeightInfo::transfer_from()]1048        #[transactional]1049        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {10501051            let sender = ensure_signed(origin)?;1052            let target_collection = Self::get_collection(collection_id)?;10531054            // Check approval1055            let approval: u128 = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));10561057            // Limits check1058            Self::is_correct_transfer(&target_collection, &recipient)?;10591060            // Transfer permissions check         1061            ensure!(1062                approval >= value || 1063                (1064                    target_collection.limits.owner_can_transfer &&1065                    Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1066                ),1067                Error::<T>::NoPermission1068            );10691070            if target_collection.access == AccessMode::WhiteList {1071                Self::check_white_list(&target_collection, &sender)?;1072                Self::check_white_list(&target_collection, &recipient)?;1073            }10741075            // Reduce approval by transferred amount or remove if remaining approval drops to 01076            if approval.saturating_sub(value) > 0 {1077                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1078            }1079            else {1080                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1081            }10821083            match target_collection.mode1084            {1085                CollectionMode::NFT => Self::transfer_nft(&target_collection, item_id, from.clone(), recipient.clone())?,1086                CollectionMode::Fungible(_)  => Self::transfer_fungible(&target_collection, value, &from, &recipient)?,1087                CollectionMode::ReFungible  => Self::transfer_refungible(&target_collection, item_id, value, from.clone(), recipient.clone())?,1088                _ => ()1089            };10901091            Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, from, recipient, value));1092            Ok(())1093        }10941095        // #[weight = 0]1096        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {10971098        //     // let no_perm_mes = "You do not have permissions to modify this collection";1099        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1100        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1101        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11021103        //     // // on_nft_received  call11041105        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;11061107        //     Ok(())1108        // }11091110        /// Set off-chain data schema.1111        /// 1112        /// # Permissions1113        /// 1114        /// * Collection Owner1115        /// * Collection Admin1116        /// 1117        /// # Arguments1118        /// 1119        /// * collection_id.1120        /// 1121        /// * schema: String representing the offchain data schema.1122        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1123        #[transactional]1124        pub fn set_variable_meta_data (1125            origin,1126            collection_id: CollectionId,1127            item_id: TokenId,1128            data: Vec<u8>1129        ) -> DispatchResult {1130            let sender = ensure_signed(origin)?;1131            1132            let target_collection = Self::get_collection(collection_id)?;1133            Self::token_exists(&target_collection, item_id)?;11341135            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);11361137            // Modify permissions check1138            ensure!(Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1139                Self::is_owner_or_admin_permissions(&target_collection, sender.clone()),1140                Error::<T>::NoPermission);11411142            match target_collection.mode1143            {1144                CollectionMode::NFT => Self::set_nft_variable_data(&target_collection, item_id, data)?,1145                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(&target_collection, item_id, data)?,1146                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1147                _ => fail!(Error::<T>::UnexpectedCollectionType)1148            };11491150            Ok(())1151        }1152 1153        /// Set schema standard1154        /// ImageURL1155        /// Unique1156        /// 1157        /// # Permissions1158        /// 1159        /// * Collection Owner1160        /// * Collection Admin1161        /// 1162        /// # Arguments1163        /// 1164        /// * collection_id.1165        /// 1166        /// * schema: SchemaVersion: enum1167        #[weight = <T as Config>::WeightInfo::set_schema_version()]1168        #[transactional]1169        pub fn set_schema_version(1170            origin,1171            collection_id: CollectionId,1172            version: SchemaVersion1173        ) -> DispatchResult {1174            let sender = ensure_signed(origin)?;1175            let mut target_collection = Self::get_collection(collection_id)?;1176            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;1177            target_collection.schema_version = version;1178            Self::save_collection(target_collection);11791180            Ok(())1181        }11821183        /// Set off-chain data schema.1184        /// 1185        /// # Permissions1186        /// 1187        /// * Collection Owner1188        /// * Collection Admin1189        /// 1190        /// # Arguments1191        /// 1192        /// * collection_id.1193        /// 1194        /// * schema: String representing the offchain data schema.1195        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1196        #[transactional]1197        pub fn set_offchain_schema(1198            origin,1199            collection_id: CollectionId,1200            schema: Vec<u8>1201        ) -> DispatchResult {1202            let sender = ensure_signed(origin)?;1203            let mut target_collection = Self::get_collection(collection_id)?;1204            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;12051206            // check schema limit1207            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");12081209            target_collection.offchain_schema = schema;1210            Self::save_collection(target_collection);12111212            Ok(())1213        }12141215        /// Set const on-chain data schema.1216        /// 1217        /// # Permissions1218        /// 1219        /// * Collection Owner1220        /// * Collection Admin1221        /// 1222        /// # Arguments1223        /// 1224        /// * collection_id.1225        /// 1226        /// * schema: String representing the const on-chain data schema.1227        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1228        #[transactional]1229        pub fn set_const_on_chain_schema (1230            origin,1231            collection_id: CollectionId,1232            schema: Vec<u8>1233        ) -> DispatchResult {1234            let sender = ensure_signed(origin)?;1235            let mut target_collection = Self::get_collection(collection_id)?;1236            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;12371238            // check schema limit1239            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");12401241            target_collection.const_on_chain_schema = schema;1242            Self::save_collection(target_collection);12431244            Ok(())1245        }12461247        /// Set variable on-chain data schema.1248        /// 1249        /// # Permissions1250        /// 1251        /// * Collection Owner1252        /// * Collection Admin1253        /// 1254        /// # Arguments1255        /// 1256        /// * collection_id.1257        /// 1258        /// * schema: String representing the variable on-chain data schema.1259        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1260        #[transactional]1261        pub fn set_variable_on_chain_schema (1262            origin,1263            collection_id: CollectionId,1264            schema: Vec<u8>1265        ) -> DispatchResult {1266            let sender = ensure_signed(origin)?;1267            let mut target_collection = Self::get_collection(collection_id)?;1268            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;12691270            // check schema limit1271            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");12721273            target_collection.variable_on_chain_schema = schema;1274            Self::save_collection(target_collection);12751276            Ok(())1277        }12781279        // Sudo permissions function1280        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1281        #[transactional]1282        pub fn set_chain_limits(1283            origin,1284            limits: ChainLimits1285        ) -> DispatchResult {12861287            #[cfg(not(feature = "runtime-benchmarks"))]1288            ensure_root(origin)?;12891290            <ChainLimit>::put(limits);1291            Ok(())1292        }12931294        /// Enable smart contract self-sponsoring.1295        /// 1296        /// # Permissions1297        /// 1298        /// * Contract Owner1299        /// 1300        /// # Arguments1301        /// 1302        /// * contract address1303        /// * enable flag1304        /// 1305        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1306        #[transactional]1307        pub fn enable_contract_sponsoring(1308            origin,1309            contract_address: T::AccountId,1310            enable: bool1311        ) -> DispatchResult {13121313            let sender = ensure_signed(origin)?;13141315            #[cfg(feature = "runtime-benchmarks")]1316            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13171318            Self::ensure_contract_owned(sender, &contract_address)?;13191320            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1321            Ok(())1322        }13231324        /// Set the rate limit for contract sponsoring to specified number of blocks.1325        /// 1326        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1327        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1328        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1329        /// from contract endowment if there are at least B blocks between such transactions. 1330        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1331        /// 1332        /// # Permissions1333        /// 1334        /// * Contract Owner1335        /// 1336        /// # Arguments1337        /// 1338        /// -`contract_address`: Address of the contract to sponsor1339        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1340        /// 1341        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1342        #[transactional]1343        pub fn set_contract_sponsoring_rate_limit(1344            origin,1345            contract_address: T::AccountId,1346            rate_limit: T::BlockNumber1347        ) -> DispatchResult {1348            let sender = ensure_signed(origin)?;13491350            #[cfg(feature = "runtime-benchmarks")]1351            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13521353            Self::ensure_contract_owned(sender, &contract_address)?;1354            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1355            Ok(())1356        }13571358        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1359        /// 1360        /// # Permissions1361        /// 1362        /// * Address that deployed smart contract.1363        /// 1364        /// # Arguments1365        /// 1366        /// -`contract_address`: Address of the contract.1367        /// 1368        /// - `enable`: .  1369        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1370        #[transactional]1371        pub fn toggle_contract_white_list(1372            origin,1373            contract_address: T::AccountId,1374            enable: bool1375        ) -> DispatchResult {1376            let sender = ensure_signed(origin)?;13771378            #[cfg(feature = "runtime-benchmarks")]1379            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13801381            Self::ensure_contract_owned(sender, &contract_address)?;1382            if enable {1383                <ContractWhiteListEnabled<T>>::insert(contract_address, true);1384            } else {1385                <ContractWhiteListEnabled<T>>::remove(contract_address);1386            }1387            Ok(())1388        }1389        1390        /// Add an address to smart contract white list.1391        /// 1392        /// # Permissions1393        /// 1394        /// * Address that deployed smart contract.1395        /// 1396        /// # Arguments1397        /// 1398        /// -`contract_address`: Address of the contract.1399        ///1400        /// -`account_address`: Address to add.1401        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1402        #[transactional]1403        pub fn add_to_contract_white_list(1404            origin,1405            contract_address: T::AccountId,1406            account_address: T::AccountId1407        ) -> DispatchResult {1408            let sender = ensure_signed(origin)?;14091410            #[cfg(feature = "runtime-benchmarks")]1411            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1412            1413            Self::ensure_contract_owned(sender, &contract_address)?;      1414            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1415            Ok(())1416        }14171418        /// Remove an address from smart contract white list.1419        /// 1420        /// # Permissions1421        /// 1422        /// * Address that deployed smart contract.1423        /// 1424        /// # Arguments1425        /// 1426        /// -`contract_address`: Address of the contract.1427        ///1428        /// -`account_address`: Address to remove.1429        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1430        #[transactional]1431        pub fn remove_from_contract_white_list(1432            origin,1433            contract_address: T::AccountId,1434            account_address: T::AccountId1435        ) -> DispatchResult {1436            let sender = ensure_signed(origin)?;14371438            #[cfg(feature = "runtime-benchmarks")]1439            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14401441            Self::ensure_contract_owned(sender, &contract_address)?;1442            <ContractWhiteList<T>>::remove(contract_address, account_address);1443            Ok(())1444        }14451446        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1447        #[transactional]1448        pub fn set_collection_limits(1449            origin,1450            collection_id: u32,1451            new_limits: CollectionLimits<T::BlockNumber>,1452        ) -> DispatchResult {1453            let sender = ensure_signed(origin)?;1454            let mut target_collection = Self::get_collection(collection_id)?;1455            Self::check_owner_permissions(&target_collection, sender.clone())?;1456            let old_limits = &target_collection.limits;1457            let chain_limits = ChainLimit::get();14581459            // collection bounds1460            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1461                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1462                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1463                Error::<T>::CollectionLimitBoundsExceeded);14641465            // token_limit   check  prev1466            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1467            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);14681469            ensure!(1470                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1471                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1472                Error::<T>::OwnerPermissionsCantBeReverted,1473            );14741475            target_collection.limits = new_limits;1476            Self::save_collection(target_collection);14771478            Ok(())1479        } 1480    }1481}14821483impl<T: Config> Module<T> {1484    pub fn create_item_internal(sender: T::AccountId, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1485        let target_collection = Self::get_collection(collection_id)?;14861487        Self::can_create_items_in_collection(&target_collection, &sender, &owner, 1)?;1488        Self::validate_create_item_args(&target_collection, &data)?;1489        Self::create_item_no_validation(&target_collection, owner, data)?;14901491        Ok(())1492    }14931494    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1495        // Limits check1496        Self::is_correct_transfer(target_collection, &recipient)?;14971498        // Transfer permissions check1499        ensure!(Self::is_item_owner(sender.clone(), target_collection, item_id) ||1500            Self::is_owner_or_admin_permissions(target_collection, sender.clone()),1501            Error::<T>::NoPermission);15021503        if target_collection.access == AccessMode::WhiteList {1504            Self::check_white_list(target_collection, &sender)?;1505            Self::check_white_list(target_collection, &recipient)?;1506        }15071508        match target_collection.mode1509        {1510            CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1511            CollectionMode::Fungible(_)  => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1512            CollectionMode::ReFungible  => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1513            _ => ()1514        };15151516        Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender, recipient, value));15171518        Ok(())1519    }152015211522    fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::AccountId) -> DispatchResult {1523        let collection_id = collection.id;15241525        // check token limit and account token limit1526        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1527        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1528        1529        Ok(())1530    }15311532    fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::AccountId, owner: &T::AccountId, amount: u32) -> DispatchResult {1533        let collection_id = collection.id;15341535        // check token limit and account token limit1536        let total_items: u32 = ItemListIndex::get(collection_id)1537            .checked_add(amount)1538            .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1539        let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner).len() as u32)1540            .checked_add(amount)1541            .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1542        ensure!(collection.limits.token_limit >= total_items,  Error::<T>::CollectionTokenLimitExceeded);1543        ensure!(collection.limits.account_token_ownership_limit >= account_items,  Error::<T>::AccountTokenLimitExceeded);15441545        if !Self::is_owner_or_admin_permissions(collection, sender.clone()) {1546            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1547            Self::check_white_list(collection, owner)?;1548            Self::check_white_list(collection, sender)?;1549        }15501551        Ok(())1552    }15531554    fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1555        match target_collection.mode1556        {1557            CollectionMode::NFT => {1558                if let CreateItemData::NFT(data) = data {1559                    // check sizes1560                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1561                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1562                } else {1563                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1564                }1565            },1566            CollectionMode::Fungible(_) => {1567                if let CreateItemData::Fungible(_) = data {1568                } else {1569                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1570                }1571            },1572            CollectionMode::ReFungible => {1573                if let CreateItemData::ReFungible(data) = data {15741575                    // check sizes1576                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1577                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);15781579                    // Check refungibility limits1580                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1581                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1582                } else {1583                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1584                }1585            },1586            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1587        };15881589        Ok(())1590    }15911592    fn create_item_no_validation(collection: &CollectionHandle<T>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1593        match data1594        {1595            CreateItemData::NFT(data) => {1596                let item = NftItemType {1597                    owner: owner.clone(),1598                    const_data: data.const_data,1599                    variable_data: data.variable_data1600                };16011602                Self::add_nft_item(collection, item)?;1603            },1604            CreateItemData::Fungible(data) => {1605                Self::add_fungible_item(collection, &owner, data.value)?;1606            },1607            CreateItemData::ReFungible(data) => {1608                let mut owner_list = Vec::new();1609                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});16101611                let item = ReFungibleItemType {1612                    owner: owner_list,1613                    const_data: data.const_data,1614                    variable_data: data.variable_data1615                };16161617                Self::add_refungible_item(collection, item)?;1618            }1619        };16201621        Ok(())1622    }16231624    fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::AccountId, value: u128) -> DispatchResult {1625        let collection_id = collection.id;16261627        // Does new owner already have an account?1628        let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner).value;16291630        // Mint 1631        let item = FungibleItemType {1632            value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1633        };1634        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16351636        // Update balance1637        let new_balance = <Balance<T>>::get(collection_id, owner)1638            .checked_add(value)1639            .ok_or(Error::<T>::NumOverflow)?;1640        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);16411642        Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1643        Ok(())1644    }16451646    fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1647        let collection_id = collection.id;16481649        let current_index = <ItemListIndex>::get(collection_id)1650            .checked_add(1)1651            .ok_or(Error::<T>::NumOverflow)?;1652        let itemcopy = item.clone();16531654        ensure!(1655            item.owner.len() == 1,1656            Error::<T>::BadCreateRefungibleCall,1657        );1658        let item_owner = item.owner.first().expect("only one owner is defined");16591660        let value = item_owner.fraction;1661        let owner = item_owner.owner.clone();16621663        Self::add_token_index(collection_id, current_index, &owner)?;16641665        <ItemListIndex>::insert(collection_id, current_index);1666        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);16671668        // Update balance1669        let new_balance = <Balance<T>>::get(collection_id, &owner)1670            .checked_add(value)1671            .ok_or(Error::<T>::NumOverflow)?;1672        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);16731674        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1675        Ok(())1676    }16771678    fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::AccountId>) -> DispatchResult {1679        let collection_id = collection.id;16801681        let current_index = <ItemListIndex>::get(collection_id)1682            .checked_add(1)1683            .ok_or(Error::<T>::NumOverflow)?;16841685        let item_owner = item.owner.clone();1686        Self::add_token_index(collection_id, current_index, &item.owner)?;16871688        <ItemListIndex>::insert(collection_id, current_index);1689        <NftItemList<T>>::insert(collection_id, current_index, item);16901691        // Update balance1692        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1693            .checked_add(1)1694            .ok_or(Error::<T>::NumOverflow)?;1695        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);16961697        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));1698        Ok(())1699    }17001701    fn burn_refungible_item(1702        collection: &CollectionHandle<T>,1703        item_id: TokenId,1704        owner: &T::AccountId,1705    ) -> DispatchResult {1706        let collection_id = collection.id;17071708        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1709            .ok_or(Error::<T>::TokenNotFound)?;1710        let rft_balance = token1711            .owner1712            .iter()1713            .find(|&i| i.owner == *owner)1714            .ok_or(Error::<T>::TokenNotFound)?;1715        Self::remove_token_index(collection_id, item_id, owner)?;17161717        // update balance1718        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1719            .checked_sub(rft_balance.fraction)1720            .ok_or(Error::<T>::NumOverflow)?;1721        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);17221723        // Re-create owners list with sender removed1724        let index = token1725            .owner1726            .iter()1727            .position(|i| i.owner == *owner)1728            .expect("owned item is exists");1729        token.owner.remove(index);1730        let owner_count = token.owner.len();17311732        // Burn the token completely if this was the last (only) owner1733        if owner_count == 0 {1734            <ReFungibleItemList<T>>::remove(collection_id, item_id);1735            <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1736        }1737        else {1738            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1739        }17401741        Ok(())1742    }17431744    fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1745        let collection_id = collection.id;17461747        let item = <NftItemList<T>>::get(collection_id, item_id)1748            .ok_or(Error::<T>::TokenNotFound)?;1749        Self::remove_token_index(collection_id, item_id, &item.owner)?;17501751        // update balance1752        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1753            .checked_sub(1)1754            .ok_or(Error::<T>::NumOverflow)?;1755        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1756        <NftItemList<T>>::remove(collection_id, item_id);1757        <VariableMetaDataBasket<T>>::remove(collection_id, item_id);17581759        Ok(())1760    }17611762    fn burn_fungible_item(owner: &T::AccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {1763        let collection_id = collection.id;17641765        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1766        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);17671768        // update balance1769        let new_balance = <Balance<T>>::get(collection_id, owner)1770            .checked_sub(value)1771            .ok_or(Error::<T>::NumOverflow)?;1772        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17731774        if balance.value - value > 0 {1775            balance.value -= value;1776            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1777        }1778        else {1779            <FungibleItemList<T>>::remove(collection_id, owner);1780        }17811782        Ok(())1783    }17841785    pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1786        Ok(<CollectionById<T>>::get(collection_id)1787            .map(|collection| CollectionHandle {1788                id: collection_id,1789                collection1790            })1791            .ok_or(Error::<T>::CollectionNotFound)?)1792    }17931794    fn save_collection(collection: CollectionHandle<T>) {1795        <CollectionById<T>>::insert(collection.id, collection.collection);1796    }17971798    fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: T::AccountId) -> DispatchResult {1799        ensure!(1800            subject == target_collection.owner,1801            Error::<T>::NoPermission1802        );18031804        Ok(())1805    }18061807    fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: T::AccountId) -> bool {1808        subject == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)1809    }18101811    fn check_owner_or_admin_permissions(1812        collection: &CollectionHandle<T>,1813        subject: T::AccountId,1814    ) -> DispatchResult {1815        ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);18161817        Ok(())1818    }18191820    fn owned_amount(1821        subject: T::AccountId,1822        target_collection: &CollectionHandle<T>,1823        item_id: TokenId,1824    ) -> Option<u128> {1825        let collection_id = target_collection.id;18261827        match target_collection.mode {1828            CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == subject)1829                .then(|| 1),1830            CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject)1831                .value),1832            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1833                .owner1834                .iter()1835                .find(|i| i.owner == subject)1836                .map(|i| i.fraction),1837            CollectionMode::Invalid => None,1838        }1839    }18401841    fn is_item_owner(subject: T::AccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {1842        match target_collection.mode {1843            CollectionMode::Fungible(_) => true,1844            _ => Self::owned_amount(subject, target_collection, item_id).is_some(),1845        }1846    }18471848    fn check_white_list(collection: &CollectionHandle<T>, address: &T::AccountId) -> DispatchResult {1849        let collection_id = collection.id;18501851        let mes = Error::<T>::AddresNotInWhiteList;1852        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);18531854        Ok(())1855    }18561857    /// Check if token exists. In case of Fungible, check if there is an entry for 1858    /// the owner in fungible balances double map1859    fn token_exists(1860        target_collection: &CollectionHandle<T>,1861        item_id: TokenId,1862    ) -> DispatchResult {1863        let collection_id = target_collection.id;1864        let exists = match target_collection.mode1865        {1866            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1867            CollectionMode::Fungible(_)  => true,1868            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1869            _ => false1870        };18711872        ensure!(exists == true, Error::<T>::TokenNotFound);1873        Ok(())1874    }18751876    fn transfer_fungible(1877        collection: &CollectionHandle<T>,1878        value: u128,1879        owner: &T::AccountId,1880        recipient: &T::AccountId,1881    ) -> DispatchResult {1882        let collection_id = collection.id;18831884        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1885        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);18861887        // Send balance to recipient (updates balanceOf of recipient)1888        Self::add_fungible_item(collection, recipient, value)?;18891890        // update balanceOf of sender1891        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);18921893        // Reduce or remove sender1894        if balance.value == value {1895            <FungibleItemList<T>>::remove(collection_id, owner);1896        }1897        else {1898            balance.value -= value;1899            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1900        }19011902        Ok(())1903    }19041905    fn transfer_refungible(1906        collection: &CollectionHandle<T>,1907        item_id: TokenId,1908        value: u128,1909        owner: T::AccountId,1910        new_owner: T::AccountId,1911    ) -> DispatchResult {1912        let collection_id = collection.id;1913        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)1914            .ok_or(Error::<T>::TokenNotFound)?;19151916        let item = full_item1917            .owner1918            .iter()1919            .filter(|i| i.owner == owner)1920            .next()1921            .ok_or(Error::<T>::TokenNotFound)?;1922        let amount = item.fraction;19231924        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19251926        // update balance1927        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1928            .checked_sub(value)1929            .ok_or(Error::<T>::NumOverflow)?;1930        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19311932        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1933            .checked_add(value)1934            .ok_or(Error::<T>::NumOverflow)?;1935        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19361937        let old_owner = item.owner.clone();1938        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19391940        // transfer1941        if amount == value && !new_owner_has_account {1942            // change owner1943            // new owner do not have account1944            let mut new_full_item = full_item.clone();1945            new_full_item1946                .owner1947                .iter_mut()1948                .find(|i| i.owner == owner)1949                .expect("old owner does present in refungible")1950                .owner = new_owner.clone();1951            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19521953            // update index collection1954            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;1955        } else {1956            let mut new_full_item = full_item.clone();1957            new_full_item1958                .owner1959                .iter_mut()1960                .find(|i| i.owner == owner)1961                .expect("old owner does present in refungible")1962                .fraction -= value;19631964            // separate amount1965            if new_owner_has_account {1966                // new owner has account1967                new_full_item1968                    .owner1969                    .iter_mut()1970                    .find(|i| i.owner == new_owner)1971                    .expect("new owner has account")1972                    .fraction += value;1973            } else {1974                // new owner do not have account1975                new_full_item.owner.push(Ownership {1976                    owner: new_owner.clone(),1977                    fraction: value,1978                });1979                Self::add_token_index(collection_id, item_id, &new_owner)?;1980            }19811982            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1983        }19841985        Ok(())1986    }19871988    fn transfer_nft(1989        collection: &CollectionHandle<T>,1990        item_id: TokenId,1991        sender: T::AccountId,1992        new_owner: T::AccountId,1993    ) -> DispatchResult {1994        let collection_id = collection.id;1995        let mut item = <NftItemList<T>>::get(collection_id, item_id)1996            .ok_or(Error::<T>::TokenNotFound)?;19971998        ensure!(1999            sender == item.owner,2000            Error::<T>::MustBeTokenOwner2001        );20022003        // update balance2004        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2005            .checked_sub(1)2006            .ok_or(Error::<T>::NumOverflow)?;2007        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20082009        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2010            .checked_add(1)2011            .ok_or(Error::<T>::NumOverflow)?;2012        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20132014        // change owner2015        let old_owner = item.owner.clone();2016        item.owner = new_owner.clone();2017        <NftItemList<T>>::insert(collection_id, item_id, item);20182019        // update index collection2020        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;20212022        Ok(())2023    }2024    2025    fn set_re_fungible_variable_data(2026        collection: &CollectionHandle<T>,2027        item_id: TokenId,2028        data: Vec<u8>2029    ) -> DispatchResult {2030        let collection_id = collection.id;2031        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2032            .ok_or(Error::<T>::TokenNotFound)?;20332034        item.variable_data = data;20352036        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20372038        Ok(())2039    }20402041    fn set_nft_variable_data(2042        collection: &CollectionHandle<T>,2043        item_id: TokenId,2044        data: Vec<u8>2045    ) -> DispatchResult {2046        let collection_id = collection.id;2047        let mut item = <NftItemList<T>>::get(collection_id, item_id)2048            .ok_or(Error::<T>::TokenNotFound)?;2049        2050        item.variable_data = data;20512052        <NftItemList<T>>::insert(collection_id, item_id, item);2053        2054        Ok(())2055    }20562057    fn init_collection(item: &Collection<T>) {2058        // check params2059        assert!(2060            item.decimal_points <= MAX_DECIMAL_POINTS,2061            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2062        );2063        assert!(2064            item.name.len() <= 64,2065            "Collection name can not be longer than 63 char"2066        );2067        assert!(2068            item.name.len() <= 256,2069            "Collection description can not be longer than 255 char"2070        );2071        assert!(2072            item.token_prefix.len() <= 16,2073            "Token prefix can not be longer than 15 char"2074        );20752076        // Generate next collection ID2077        let next_id = CreatedCollectionCount::get()2078            .checked_add(1)2079            .unwrap();20802081        CreatedCollectionCount::put(next_id);2082    }20832084    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2085        let current_index = <ItemListIndex>::get(collection_id)2086            .checked_add(1)2087            .unwrap();20882089        let item_owner = item.owner.clone();2090        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();20912092        <ItemListIndex>::insert(collection_id, current_index);20932094        // Update balance2095        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2096            .checked_add(1)2097            .unwrap();2098        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2099    }21002101    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2102        let current_index = <ItemListIndex>::get(collection_id)2103            .checked_add(1)2104            .unwrap();21052106        Self::add_token_index(collection_id, current_index, owner).unwrap();21072108        <ItemListIndex>::insert(collection_id, current_index);21092110        // Update balance2111        let new_balance = <Balance<T>>::get(collection_id, owner)2112            .checked_add(item.value)2113            .unwrap();2114        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2115    }21162117    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2118        let current_index = <ItemListIndex>::get(collection_id)2119            .checked_add(1)2120            .unwrap();21212122        let value = item.owner.first().unwrap().fraction;2123        let owner = item.owner.first().unwrap().owner.clone();21242125        Self::add_token_index(collection_id, current_index, &owner).unwrap();21262127        <ItemListIndex>::insert(collection_id, current_index);21282129        // Update balance2130        let new_balance = <Balance<T>>::get(collection_id, &owner)2131            .checked_add(value)2132            .unwrap();2133        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2134    }21352136    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {2137        // add to account limit2138        if <AccountItemCount<T>>::contains_key(owner) {21392140            // bound Owned tokens by a single address2141            let count = <AccountItemCount<T>>::get(owner);2142            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21432144            <AccountItemCount<T>>::insert(owner.clone(), count2145                .checked_add(1)2146                .ok_or(Error::<T>::NumOverflow)?);2147        }2148        else {2149            <AccountItemCount<T>>::insert(owner.clone(), 1);2150        }21512152        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2153        if list_exists {2154            let mut list = <AddressTokens<T>>::get(collection_id, owner);2155            let item_contains = list.contains(&item_index.clone());21562157            if !item_contains {2158                list.push(item_index.clone());2159            }21602161            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2162        } else {2163            let mut itm = Vec::new();2164            itm.push(item_index.clone());2165            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2166        }21672168        Ok(())2169    }21702171    fn remove_token_index(2172        collection_id: CollectionId,2173        item_index: TokenId,2174        owner: &T::AccountId,2175    ) -> DispatchResult {21762177        // update counter2178        <AccountItemCount<T>>::insert(owner.clone(), 2179            <AccountItemCount<T>>::get(owner)2180            .checked_sub(1)2181            .ok_or(Error::<T>::NumOverflow)?);218221832184        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2185        if list_exists {2186            let mut list = <AddressTokens<T>>::get(collection_id, owner);2187            let item_contains = list.contains(&item_index.clone());21882189            if item_contains {2190                list.retain(|&item| item != item_index);2191                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2192            }2193        }21942195        Ok(())2196    }21972198    fn move_token_index(2199        collection_id: CollectionId,2200        item_index: TokenId,2201        old_owner: &T::AccountId,2202        new_owner: &T::AccountId,2203    ) -> DispatchResult {2204        Self::remove_token_index(collection_id, item_index, old_owner)?;2205        Self::add_token_index(collection_id, item_index, new_owner)?;22062207        Ok(())2208    }2209    2210    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2211        ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);22122213        Ok(())2214    }2215}