git.delta.rocks / unique-network / refs/commits / 4dbeda6769b2

difftreelog

source

pallets/nft/src/lib.rs87.5 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)]910extern crate alloc;1112pub use serde::{Serialize, Deserialize};1314pub use frame_support::{15    construct_runtime, decl_event, decl_module, decl_storage, decl_error,16    dispatch::DispatchResult,17    ensure, fail, parameter_types,18    traits::{19        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,20        Randomness, IsSubType, WithdrawReasons,21    },22    weights::{23        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},24        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,25        WeightToFeePolynomial, DispatchClass,26    },27    StorageValue,28    transactional,29};3031use frame_system::{self as system, ensure_signed, ensure_root};32use sp_core::H160;33use sp_runtime::sp_std::prelude::Vec;34use core::ops::{Deref, DerefMut};35use nft_data_structs::{36    MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,37	AccessMode, ChainLimits, Collection, CreateItemData, CollectionLimits,38    CollectionId, CollectionMode, TokenId, 39    SchemaVersion, SponsorshipState, Ownership,40    NftItemType, FungibleItemType, ReFungibleItemType41};4243#[cfg(test)]44mod mock;4546#[cfg(test)]47mod tests;4849mod default_weights;50mod eth;5152pub use eth::NftErcSupport;53pub use eth::account::*;54use eth::erc::{ERC20Events, ERC721Events};5556#[cfg(feature = "runtime-benchmarks")]57mod benchmarking;5859pub trait WeightInfo {60	fn create_collection() -> Weight;61	fn destroy_collection() -> Weight;62	fn add_to_white_list() -> Weight;63	fn remove_from_white_list() -> Weight;64    fn set_public_access_mode() -> Weight;65    fn set_mint_permission() -> Weight;66    fn change_collection_owner() -> Weight;67    fn add_collection_admin() -> Weight;68    fn remove_collection_admin() -> Weight;69    fn set_collection_sponsor() -> Weight;70    fn confirm_sponsorship() -> Weight;71    fn remove_collection_sponsor() -> Weight;72    fn create_item(s: usize) -> Weight;73    fn burn_item() -> Weight;74    fn transfer() -> Weight;75    fn approve() -> Weight;76    fn transfer_from() -> Weight;77    fn set_offchain_schema() -> Weight;78    fn set_const_on_chain_schema() -> Weight;79    fn set_variable_on_chain_schema() -> Weight;80    fn set_variable_meta_data() -> Weight;81    fn enable_contract_sponsoring() -> Weight;82    fn set_schema_version() -> Weight;83    fn set_chain_limits() -> Weight;84    fn set_contract_sponsoring_rate_limit() -> Weight;85    fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;86    fn toggle_contract_white_list() -> Weight;87    fn add_to_contract_white_list() -> Weight;88    fn remove_from_contract_white_list() -> Weight;89    fn set_collection_limits() -> Weight;90}9192decl_error! {93	/// Error for non-fungible-token module.94	pub enum Error for Module<T: Config> {95        /// Total collections bound exceeded.96        TotalCollectionsLimitExceeded,97		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.98        CollectionDecimalPointLimitExceeded, 99        /// Collection name can not be longer than 63 char.100        CollectionNameLimitExceeded, 101        /// Collection description can not be longer than 255 char.102        CollectionDescriptionLimitExceeded, 103        /// Token prefix can not be longer than 15 char.104        CollectionTokenPrefixLimitExceeded,105        /// This collection does not exist.106        CollectionNotFound,107        /// Item not exists.108        TokenNotFound,109        /// Admin not found110        AdminNotFound,111        /// Arithmetic calculation overflow.112        NumOverflow,       113        /// Account already has admin role.114        AlreadyAdmin,  115        /// You do not own this collection.116        NoPermission,117        /// This address is not set as sponsor, use setCollectionSponsor first.118        ConfirmUnsetSponsorFail,119        /// Collection is not in mint mode.120        PublicMintingNotAllowed,121        /// Sender parameter and item owner must be equal.122        MustBeTokenOwner,123        /// Item balance not enough.124        TokenValueTooLow,125        /// Size of item is too large.126        NftSizeLimitExceeded,127        /// No approve found128        ApproveNotFound,129        /// Requested value more than approved.130        TokenValueNotEnough,131        /// Only approved addresses can call this method.132        ApproveRequired,133        /// Address is not in white list.134        AddresNotInWhiteList,135        /// Number of collection admins bound exceeded.136        CollectionAdminsLimitExceeded,137        /// Owned tokens by a single address bound exceeded.138        AddressOwnershipLimitExceeded,139        /// Length of items properties must be greater than 0.140        EmptyArgument,141        /// const_data exceeded data limit.142        TokenConstDataLimitExceeded,143        /// variable_data exceeded data limit.144        TokenVariableDataLimitExceeded,145        /// Not NFT item data used to mint in NFT collection.146        NotNftDataUsedToMintNftCollectionToken,147        /// Not Fungible item data used to mint in Fungible collection.148        NotFungibleDataUsedToMintFungibleCollectionToken,149        /// Not Re Fungible item data used to mint in Re Fungible collection.150        NotReFungibleDataUsedToMintReFungibleCollectionToken,151        /// Unexpected collection type.152        UnexpectedCollectionType,153        /// Can't store metadata in fungible tokens.154        CantStoreMetadataInFungibleTokens,155        /// Collection token limit exceeded156        CollectionTokenLimitExceeded,157        /// Account token limit exceeded per collection158        AccountTokenLimitExceeded,159        /// Collection limit bounds per collection exceeded160        CollectionLimitBoundsExceeded,161        /// Tried to enable permissions which are only permitted to be disabled162        OwnerPermissionsCantBeReverted,163        /// Schema data size limit bound exceeded164        SchemaDataLimitExceeded,165        /// Maximum refungibility exceeded166        WrongRefungiblePieces,167        /// createRefungible should be called with one owner168        BadCreateRefungibleCall,169        /// Gas limit exceeded170        OutOfGas,171	}172}173174pub struct CollectionHandle<T: system::Config> {175    pub id: CollectionId,176    pub collection: Collection<T>,177}178179impl<T: frame_system::Config> Deref for CollectionHandle<T> {180    type Target = Collection<T>;181182    fn deref(&self) -> &Self::Target {183        &self.collection184    }185}186187impl<T: frame_system::Config> DerefMut for CollectionHandle<T> {188    fn deref_mut(&mut self) -> &mut Self::Target {189        &mut self.collection190    }191}192193pub trait Config: system::Config + Sized {194    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;195196    /// Weight information for extrinsics in this pallet.197	type WeightInfo: WeightInfo;198199    type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;200    type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;201    type EvmWithdrawOrigin: pallet_evm::EnsureAddressOrigin<Self::Origin, Success = Self::AccountId>;202203	type CrossAccountId: CrossAccountId<Self::AccountId>;204    type Currency: Currency<Self::AccountId>;205    type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;206    type TreasuryAccountId: Get<Self::AccountId>;207208    type EthereumChainId: Get<u64>;209    type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;210}211212// # Used definitions213//214// ## User control levels215//216// chain-controlled - key is uncontrolled by user217//                    i.e autoincrementing index218//                    can use non-cryptographic hash219// real - key is controlled by user220//        but it is hard to generate enough colliding values, i.e owner of signed txs221//        can use non-cryptographic hash222// controlled - key is completly controlled by users223//              i.e maps with mutable keys224//              should use cryptographic hash225//226// ## User control level downgrade reasons227//228// ?1 - chain-controlled -> controlled229//      collections/tokens can be destroyed, resulting in massive holes230// ?2 - chain-controlled -> controlled231//      same as ?1, but can be only added, resulting in easier exploitation232// ?3 - real -> controlled233//      no confirmation required, so addresses can be easily generated234decl_storage! {235    trait Store for Module<T: Config> as Nft {236237        //#region Private members238        /// Id of next collection239        CreatedCollectionCount: u32;240        /// Used for migrations241        ChainVersion: u64;242        /// Id of last collection token243        /// Collection id (controlled?1)244        ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;245        //#endregion246247        //#region Chain limits struct248        pub ChainLimit get(fn chain_limit) config(): ChainLimits;249        //#endregion250251        //#region Bound counters252        /// Amount of collections destroyed, used for total amount tracking with253        /// CreatedCollectionCount254        DestroyedCollectionCount: u32;255        /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)256        /// Account id (real)257        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;258        //#endregion259260        //#region Basic collections261        /// Collection info262        /// Collection id (controlled?1)263        pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;264        /// List of collection admins265        /// Collection id (controlled?2)266        pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;267        /// Whitelisted collection users268        /// Collection id (controlled?2), user id (controlled?3)269        pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;270        //#endregion271272        /// How many of collection items user have273        /// Collection id (controlled?2), account id (real)274        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;275276        /// Amount of items which spender can transfer out of owners account (via transferFrom)277        /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))278        /// TODO: Off chain worker should remove from this map when token gets removed279        pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;280281        //#region Item collections282        /// Collection id (controlled?2), token id (controlled?1)283        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;284        /// Collection id (controlled?2), owner (controlled?2)285        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;286        /// Collection id (controlled?2), token id (controlled?1)287        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;288        //#endregion289290        //#region Index list291        /// Collection id (controlled?2), tokens owner (controlled?2)292        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;293        //#endregion294295        //#region Tokens transfer rate limit baskets296        /// (Collection id (controlled?2), who created (real))297        /// TODO: Off chain worker should remove from this map when collection gets removed298        pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;299        /// Collection id (controlled?2), token id (controlled?2)300        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;301        /// Collection id (controlled?2), owning user (real)302        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;303        /// Collection id (controlled?2), token id (controlled?2)304        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;305        //#endregion306307        /// Variable metadata sponsoring308        /// Collection id (controlled?2), token id (controlled?2)309        pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;310      311        //#region Contract Sponsorship and Ownership312        /// Contract address (real)313        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;314        /// Contract address (real)315        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;316        /// (Contract address(real), caller (real))317        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;318        /// Contract address (real)319        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;320        /// Contract address (real)321        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 322        /// Contract address (real) => Whitelisted user (controlled?3)323        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 324        //#endregion325    }326    add_extra_genesis {327        build(|config: &GenesisConfig<T>| {328            // Modification of storage329            for (_num, _c) in &config.collection_id {330                <Module<T>>::init_collection(_c);331            }332333            for (_num, _c, _i) in &config.nft_item_id {334                <Module<T>>::init_nft_token(*_c, _i);335            }336337            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {338                <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);339            }340341            for (_num, _c, _i) in &config.refungible_item_id {342                <Module<T>>::init_refungible_token(*_c, _i);343            }344        })345    }346}347348decl_event!(349    pub enum Event<T>350    where351        AccountId = <T as frame_system::Config>::AccountId,352        CrossAccountId = <T as Config>::CrossAccountId,353    {354        /// New collection was created355        /// 356        /// # Arguments357        /// 358        /// * collection_id: Globally unique identifier of newly created collection.359        /// 360        /// * mode: [CollectionMode] converted into u8.361        /// 362        /// * account_id: Collection owner.363        CollectionCreated(CollectionId, u8, AccountId),364365        /// New item was created.366        /// 367        /// # Arguments368        /// 369        /// * collection_id: Id of the collection where item was created.370        /// 371        /// * item_id: Id of an item. Unique within the collection.372        ///373        /// * recipient: Owner of newly created item 374        ItemCreated(CollectionId, TokenId, CrossAccountId),375376        /// Collection item was burned.377        /// 378        /// # Arguments379        /// 380        /// collection_id.381        /// 382        /// item_id: Identifier of burned NFT.383        ItemDestroyed(CollectionId, TokenId),384385        /// Item was transferred386        ///387        /// * collection_id: Id of collection to which item is belong388        ///389        /// * item_id: Id of an item390        ///391        /// * sender: Original owner of item392        ///393        /// * recipient: New owner of item394        ///395        /// * amount: Always 1 for NFT396        Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),397398        /// * collection_id399        ///400        /// * item_id401        ///402        /// * sender403        ///404        /// * spender405        ///406        /// * amount407        Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),408    }409);410411decl_module! {412    pub struct Module<T: Config> for enum Call 413    where 414        origin: T::Origin415    {416        fn deposit_event() = default;417        type Error = Error<T>;418419        fn on_initialize(_now: T::BlockNumber) -> Weight {420            0421        }422423        /// 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.424        /// 425        /// # Permissions426        /// 427        /// * Anyone.428        /// 429        /// # Arguments430        /// 431        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.432        /// 433        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.434        /// 435        /// * token_prefix: UTF-8 string with token prefix.436        /// 437        /// * mode: [CollectionMode] collection type and type dependent data.438        // returns collection ID439        #[weight = <T as Config>::WeightInfo::create_collection()]440        #[transactional]441        pub fn create_collection(origin,442                                 collection_name: Vec<u16>,443                                 collection_description: Vec<u16>,444                                 token_prefix: Vec<u8>,445                                 mode: CollectionMode) -> DispatchResult {446447            // Anyone can create a collection448            let who = ensure_signed(origin)?;449450            // Take a (non-refundable) deposit of collection creation451            let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();452            imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(453                &T::TreasuryAccountId::get(),454                T::CollectionCreationPrice::get(),455            ));456            <T as Config>::Currency::settle(457                &who,458                imbalance,459                WithdrawReasons::TRANSFER,460                ExistenceRequirement::KeepAlive,461            ).map_err(|_| Error::<T>::NoPermission)?;462463            let decimal_points = match mode {464                CollectionMode::Fungible(points) => points,465                _ => 0466            };467468            let chain_limit = ChainLimit::get();469470            let created_count = CreatedCollectionCount::get();471            let destroyed_count = DestroyedCollectionCount::get();472473            // bound Total number of collections474            ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);475476            // check params477            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);478            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);479            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);480            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);481482            // Generate next collection ID483            let next_id = created_count484                .checked_add(1)485                .ok_or(Error::<T>::NumOverflow)?;486487            CreatedCollectionCount::put(next_id);488489            let limits = CollectionLimits {490                sponsored_data_size: chain_limit.custom_data_limit,491                ..Default::default()492            };493494            // Create new collection495            let new_collection = Collection {496                owner: who.clone(),497                name: collection_name,498                mode: mode.clone(),499                mint_mode: false,500                access: AccessMode::Normal,501                description: collection_description,502                decimal_points: decimal_points,503                token_prefix: token_prefix,504                offchain_schema: Vec::new(),505                schema_version: SchemaVersion::ImageURL,506                sponsorship: SponsorshipState::Disabled,507                variable_on_chain_schema: Vec::new(),508                const_on_chain_schema: Vec::new(),509                limits,510            };511512            // Add new collection to map513            <CollectionById<T>>::insert(next_id, new_collection);514515            // call event516            Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who));517518            Ok(())519        }520521        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.522        /// 523        /// # Permissions524        /// 525        /// * Collection Owner.526        /// 527        /// # Arguments528        /// 529        /// * collection_id: collection to destroy.530        #[weight = <T as Config>::WeightInfo::destroy_collection()]531        #[transactional]532        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {533534            let sender = ensure_signed(origin)?;535            let collection = Self::get_collection(collection_id)?;536            Self::check_owner_permissions(&collection, &sender)?;537            if !collection.limits.owner_can_destroy {538                fail!(Error::<T>::NoPermission);539            }540541            <AddressTokens<T>>::remove_prefix(collection_id);542            <Allowances<T>>::remove_prefix(collection_id);543            <Balance<T>>::remove_prefix(collection_id);544            <ItemListIndex>::remove(collection_id);545            <AdminList<T>>::remove(collection_id);546            <CollectionById<T>>::remove(collection_id);547            <WhiteList<T>>::remove_prefix(collection_id);548549            <NftItemList<T>>::remove_prefix(collection_id);550            <FungibleItemList<T>>::remove_prefix(collection_id);551            <ReFungibleItemList<T>>::remove_prefix(collection_id);552553            <NftTransferBasket<T>>::remove_prefix(collection_id);554            <FungibleTransferBasket<T>>::remove_prefix(collection_id);555            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);556557            <VariableMetaDataBasket<T>>::remove_prefix(collection_id);558559            DestroyedCollectionCount::put(DestroyedCollectionCount::get()560                .checked_add(1)561                .ok_or(Error::<T>::NumOverflow)?);562563            Ok(())564        }565566        /// Add an address to white list.567        /// 568        /// # Permissions569        /// 570        /// * Collection Owner571        /// * Collection Admin572        /// 573        /// # Arguments574        /// 575        /// * collection_id.576        /// 577        /// * address.578        #[weight = <T as Config>::WeightInfo::add_to_white_list()]579        #[transactional]580        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{581582            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);583            let collection = Self::get_collection(collection_id)?;584585            Self::toggle_white_list_internal(586                &sender,587                &collection,588                &address,589                true,590            )?;591592            Ok(())593        }594595        /// Remove an address from white list.596        /// 597        /// # Permissions598        /// 599        /// * Collection Owner600        /// * Collection Admin601        /// 602        /// # Arguments603        /// 604        /// * collection_id.605        /// 606        /// * address.607        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]608        #[transactional]609        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{610611            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);612            let collection = Self::get_collection(collection_id)?;613614            Self::toggle_white_list_internal(615                &sender,616                &collection,617                &address,618                false,619            )?;620621            Ok(())622        }623624        /// Toggle between normal and white list access for the methods with access for `Anyone`.625        /// 626        /// # Permissions627        /// 628        /// * Collection Owner.629        /// 630        /// # Arguments631        /// 632        /// * collection_id.633        /// 634        /// * mode: [AccessMode]635        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]636        #[transactional]637        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult638        {639            let sender = ensure_signed(origin)?;640641            let mut target_collection = Self::get_collection(collection_id)?;642            Self::check_owner_permissions(&target_collection, &sender)?;643            target_collection.access = mode;644            Self::save_collection(target_collection);645646            Ok(())647        }648649        /// Allows Anyone to create tokens if:650        /// * White List is enabled, and651        /// * Address is added to white list, and652        /// * This method was called with True parameter653        /// 654        /// # Permissions655        /// * Collection Owner656        ///657        /// # Arguments658        /// 659        /// * collection_id.660        /// 661        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.662        #[weight = <T as Config>::WeightInfo::set_mint_permission()]663        #[transactional]664        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult665        {666            let sender = ensure_signed(origin)?;667668            let mut target_collection = Self::get_collection(collection_id)?;669            Self::check_owner_permissions(&target_collection, &sender)?;670            target_collection.mint_mode = mint_permission;671            Self::save_collection(target_collection);672673            Ok(())674        }675676        /// Change the owner of the collection.677        /// 678        /// # Permissions679        /// 680        /// * Collection Owner.681        /// 682        /// # Arguments683        /// 684        /// * collection_id.685        /// 686        /// * new_owner.687        #[weight = <T as Config>::WeightInfo::change_collection_owner()]688        #[transactional]689        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {690691            let sender = ensure_signed(origin)?;692            let mut target_collection = Self::get_collection(collection_id)?;693            Self::check_owner_permissions(&target_collection, &sender)?;694            target_collection.owner = new_owner;695            Self::save_collection(target_collection);696697            Ok(())698        }699700        /// Adds an admin of the Collection.701        /// 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. 702        /// 703        /// # Permissions704        /// 705        /// * Collection Owner.706        /// * Collection Admin.707        /// 708        /// # Arguments709        /// 710        /// * collection_id: ID of the Collection to add admin for.711        /// 712        /// * new_admin_id: Address of new admin to add.713        #[weight = <T as Config>::WeightInfo::add_collection_admin()]714        #[transactional]715        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {716            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);717            let collection = Self::get_collection(collection_id)?;718            Self::check_owner_or_admin_permissions(&collection, &sender)?;719            let mut admin_arr = <AdminList<T>>::get(collection_id);720721            match admin_arr.binary_search(&new_admin_id) {722                Ok(_) => {},723                Err(idx) => {724                    let limits = ChainLimit::get();725                    ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);726                    admin_arr.insert(idx, new_admin_id);727                    <AdminList<T>>::insert(collection_id, admin_arr);728                }729            }730            Ok(())731        }732733        /// 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.734        ///735        /// # Permissions736        /// 737        /// * Collection Owner.738        /// * Collection Admin.739        /// 740        /// # Arguments741        /// 742        /// * collection_id: ID of the Collection to remove admin for.743        /// 744        /// * account_id: Address of admin to remove.745        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]746        #[transactional]747        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {748            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);749            let collection = Self::get_collection(collection_id)?;750            Self::check_owner_or_admin_permissions(&collection, &sender)?;751            let mut admin_arr = <AdminList<T>>::get(collection_id);752753            match admin_arr.binary_search(&account_id) {754                Ok(idx) => {755                    admin_arr.remove(idx);756                    <AdminList<T>>::insert(collection_id, admin_arr);757                },758                Err(_) => {}759            }760            Ok(())761        }762763        /// # Permissions764        /// 765        /// * Collection Owner766        /// 767        /// # Arguments768        /// 769        /// * collection_id.770        /// 771        /// * new_sponsor.772        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]773        #[transactional]774        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {775            let sender = ensure_signed(origin)?;776            let mut target_collection = Self::get_collection(collection_id)?;777            Self::check_owner_permissions(&target_collection, &sender)?;778779            target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);780            Self::save_collection(target_collection);781782            Ok(())783        }784785        /// # Permissions786        /// 787        /// * Sponsor.788        /// 789        /// # Arguments790        /// 791        /// * collection_id.792        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]793        #[transactional]794        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {795            let sender = ensure_signed(origin)?;796797            let mut target_collection = Self::get_collection(collection_id)?;798            ensure!(799                target_collection.sponsorship.pending_sponsor() == Some(&sender),800                Error::<T>::ConfirmUnsetSponsorFail801            );802803            target_collection.sponsorship = SponsorshipState::Confirmed(sender);804            Self::save_collection(target_collection);805806            Ok(())807        }808809        /// Switch back to pay-per-own-transaction model.810        ///811        /// # Permissions812        ///813        /// * Collection owner.814        /// 815        /// # Arguments816        /// 817        /// * collection_id.818        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]819        #[transactional]820        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {821            let sender = ensure_signed(origin)?;822823            let mut target_collection = Self::get_collection(collection_id)?;824            Self::check_owner_permissions(&target_collection, &sender)?;825826            target_collection.sponsorship = SponsorshipState::Disabled;827            Self::save_collection(target_collection);828829            Ok(())830        }831832        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.833        /// 834        /// # Permissions835        /// 836        /// * Collection Owner.837        /// * Collection Admin.838        /// * Anyone if839        ///     * White List is enabled, and840        ///     * Address is added to white list, and841        ///     * MintPermission is enabled (see SetMintPermission method)842        /// 843        /// # Arguments844        /// 845        /// * collection_id: ID of the collection.846        /// 847        /// * owner: Address, initial owner of the NFT.848        ///849        /// * data: Token data to store on chain.850        // #[weight =851        // (130_000_000 as Weight)852        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))853        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))854        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]855856        #[weight = <T as Config>::WeightInfo::create_item(data.len())]857        #[transactional]858        pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {859            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);860            let collection = Self::get_collection(collection_id)?;861862            Self::create_item_internal(&sender, &collection, &owner, data)?;863864            Self::submit_logs(collection)?;865            Ok(())866        }867868        /// This method creates multiple items in a collection created with CreateCollection method.869        /// 870        /// # Permissions871        /// 872        /// * Collection Owner.873        /// * Collection Admin.874        /// * Anyone if875        ///     * White List is enabled, and876        ///     * Address is added to white list, and877        ///     * MintPermission is enabled (see SetMintPermission method)878        /// 879        /// # Arguments880        /// 881        /// * collection_id: ID of the collection.882        /// 883        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].884        /// 885        /// * owner: Address, initial owner of the NFT.886        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()887                               .map(|data| { data.len() })888                               .sum())]889        #[transactional]890        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {891892            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);893            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);894            let collection = Self::get_collection(collection_id)?;895896            Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;897898            Self::submit_logs(collection)?;899            Ok(())900        }901902        /// Destroys a concrete instance of NFT.903        /// 904        /// # Permissions905        /// 906        /// * Collection Owner.907        /// * Collection Admin.908        /// * Current NFT Owner.909        /// 910        /// # Arguments911        /// 912        /// * collection_id: ID of the collection.913        /// 914        /// * item_id: ID of NFT to burn.915        #[weight = <T as Config>::WeightInfo::burn_item()]916        #[transactional]917        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {918919            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);920            let target_collection = Self::get_collection(collection_id)?;921922            Self::burn_item_internal(&sender, &target_collection, item_id, value)?;923924            Self::submit_logs(target_collection)?;925            Ok(())926        }927928        /// Change ownership of the token.929        /// 930        /// # Permissions931        /// 932        /// * Collection Owner933        /// * Collection Admin934        /// * Current NFT owner935        ///936        /// # Arguments937        /// 938        /// * recipient: Address of token recipient.939        /// 940        /// * collection_id.941        /// 942        /// * item_id: ID of the item943        ///     * Non-Fungible Mode: Required.944        ///     * Fungible Mode: Ignored.945        ///     * Re-Fungible Mode: Required.946        /// 947        /// * value: Amount to transfer.948        ///     * Non-Fungible Mode: Ignored949        ///     * Fungible Mode: Must specify transferred amount950        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)951        #[weight = <T as Config>::WeightInfo::transfer()]952        #[transactional]953        pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {954            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);955            let collection = Self::get_collection(collection_id)?;956957            Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;958959            Self::submit_logs(collection)?;960            Ok(())961        }962963        /// Set, change, or remove approved address to transfer the ownership of the NFT.964        /// 965        /// # Permissions966        /// 967        /// * Collection Owner968        /// * Collection Admin969        /// * Current NFT owner970        /// 971        /// # Arguments972        /// 973        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).974        /// 975        /// * collection_id.976        /// 977        /// * item_id: ID of the item.978        #[weight = <T as Config>::WeightInfo::approve()]979        #[transactional]980        pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {981            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);982            let collection = Self::get_collection(collection_id)?;983984            Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;985986            Self::submit_logs(collection)?;987            Ok(())988        }989        990        /// 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.991        /// 992        /// # Permissions993        /// * Collection Owner994        /// * Collection Admin995        /// * Current NFT owner996        /// * Address approved by current NFT owner997        /// 998        /// # Arguments999        /// 1000        /// * from: Address that owns token.1001        /// 1002        /// * recipient: Address of token recipient.1003        /// 1004        /// * collection_id.1005        /// 1006        /// * item_id: ID of the item.1007        /// 1008        /// * value: Amount to transfer.1009        #[weight = <T as Config>::WeightInfo::transfer_from()]1010        #[transactional]1011        pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1012            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1013            let collection = Self::get_collection(collection_id)?;10141015            Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10161017            Self::submit_logs(collection)?;1018            Ok(())1019        }1020        // #[weight = 0]1021        //     // let no_perm_mes = "You do not have permissions to modify this collection";1022        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1023        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1024        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);10251026        //     // // on_nft_received  call10271028        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;10291030        //     Ok(())1031        // }10321033        /// Set off-chain data schema.1034        /// 1035        /// # Permissions1036        /// 1037        /// * Collection Owner1038        /// * Collection Admin1039        /// 1040        /// # Arguments1041        /// 1042        /// * collection_id.1043        /// 1044        /// * schema: String representing the offchain data schema.1045        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1046        #[transactional]1047        pub fn set_variable_meta_data (1048            origin,1049            collection_id: CollectionId,1050            item_id: TokenId,1051            data: Vec<u8>1052        ) -> DispatchResult {1053            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1054            1055            let collection = Self::get_collection(collection_id)?;10561057            Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;10581059            Ok(())1060        }1061 1062        /// Set schema standard1063        /// ImageURL1064        /// Unique1065        /// 1066        /// # Permissions1067        /// 1068        /// * Collection Owner1069        /// * Collection Admin1070        /// 1071        /// # Arguments1072        /// 1073        /// * collection_id.1074        /// 1075        /// * schema: SchemaVersion: enum1076        #[weight = <T as Config>::WeightInfo::set_schema_version()]1077        #[transactional]1078        pub fn set_schema_version(1079            origin,1080            collection_id: CollectionId,1081            version: SchemaVersion1082        ) -> DispatchResult {1083            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1084            let mut target_collection = Self::get_collection(collection_id)?;1085            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1086            target_collection.schema_version = version;1087            Self::save_collection(target_collection);10881089            Ok(())1090        }10911092        /// Set off-chain data schema.1093        /// 1094        /// # Permissions1095        /// 1096        /// * Collection Owner1097        /// * Collection Admin1098        /// 1099        /// # Arguments1100        /// 1101        /// * collection_id.1102        /// 1103        /// * schema: String representing the offchain data schema.1104        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1105        #[transactional]1106        pub fn set_offchain_schema(1107            origin,1108            collection_id: CollectionId,1109            schema: Vec<u8>1110        ) -> DispatchResult {1111            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1112            let mut target_collection = Self::get_collection(collection_id)?;1113            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11141115            // check schema limit1116            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");11171118            target_collection.offchain_schema = schema;1119            Self::save_collection(target_collection);11201121            Ok(())1122        }11231124        /// Set const on-chain data schema.1125        /// 1126        /// # Permissions1127        /// 1128        /// * Collection Owner1129        /// * Collection Admin1130        /// 1131        /// # Arguments1132        /// 1133        /// * collection_id.1134        /// 1135        /// * schema: String representing the const on-chain data schema.1136        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1137        #[transactional]1138        pub fn set_const_on_chain_schema (1139            origin,1140            collection_id: CollectionId,1141            schema: Vec<u8>1142        ) -> DispatchResult {1143            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1144            let mut target_collection = Self::get_collection(collection_id)?;1145            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11461147            // check schema limit1148            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");11491150            target_collection.const_on_chain_schema = schema;1151            Self::save_collection(target_collection);11521153            Ok(())1154        }11551156        /// Set variable on-chain data schema.1157        /// 1158        /// # Permissions1159        /// 1160        /// * Collection Owner1161        /// * Collection Admin1162        /// 1163        /// # Arguments1164        /// 1165        /// * collection_id.1166        /// 1167        /// * schema: String representing the variable on-chain data schema.1168        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1169        #[transactional]1170        pub fn set_variable_on_chain_schema (1171            origin,1172            collection_id: CollectionId,1173            schema: Vec<u8>1174        ) -> DispatchResult {1175            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1176            let mut target_collection = Self::get_collection(collection_id)?;1177            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11781179            // check schema limit1180            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");11811182            target_collection.variable_on_chain_schema = schema;1183            Self::save_collection(target_collection);11841185            Ok(())1186        }11871188        // Sudo permissions function1189        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1190        #[transactional]1191        pub fn set_chain_limits(1192            origin,1193            limits: ChainLimits1194        ) -> DispatchResult {11951196            #[cfg(not(feature = "runtime-benchmarks"))]1197            ensure_root(origin)?;11981199            <ChainLimit>::put(limits);1200            Ok(())1201        }12021203        /// Enable smart contract self-sponsoring.1204        /// 1205        /// # Permissions1206        /// 1207        /// * Contract Owner1208        /// 1209        /// # Arguments1210        /// 1211        /// * contract address1212        /// * enable flag1213        /// 1214        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1215        #[transactional]1216        pub fn enable_contract_sponsoring(1217            origin,1218            contract_address: T::AccountId,1219            enable: bool1220        ) -> DispatchResult {12211222            let sender = ensure_signed(origin)?;12231224            #[cfg(feature = "runtime-benchmarks")]1225            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());12261227            Self::ensure_contract_owned(sender, &contract_address)?;12281229            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1230            Ok(())1231        }12321233        /// Set the rate limit for contract sponsoring to specified number of blocks.1234        /// 1235        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1236        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1237        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1238        /// from contract endowment if there are at least B blocks between such transactions. 1239        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1240        /// 1241        /// # Permissions1242        /// 1243        /// * Contract Owner1244        /// 1245        /// # Arguments1246        /// 1247        /// -`contract_address`: Address of the contract to sponsor1248        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1249        /// 1250        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1251        #[transactional]1252        pub fn set_contract_sponsoring_rate_limit(1253            origin,1254            contract_address: T::AccountId,1255            rate_limit: T::BlockNumber1256        ) -> DispatchResult {1257            let sender = ensure_signed(origin)?;12581259            #[cfg(feature = "runtime-benchmarks")]1260            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());12611262            Self::ensure_contract_owned(sender, &contract_address)?;1263            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1264            Ok(())1265        }12661267        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1268        /// 1269        /// # Permissions1270        /// 1271        /// * Address that deployed smart contract.1272        /// 1273        /// # Arguments1274        /// 1275        /// -`contract_address`: Address of the contract.1276        /// 1277        /// - `enable`: .  1278        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1279        #[transactional]1280        pub fn toggle_contract_white_list(1281            origin,1282            contract_address: T::AccountId,1283            enable: bool1284        ) -> DispatchResult {1285            let sender = ensure_signed(origin)?;12861287            #[cfg(feature = "runtime-benchmarks")]1288            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());12891290            Self::ensure_contract_owned(sender, &contract_address)?;1291            if enable {1292                <ContractWhiteListEnabled<T>>::insert(contract_address, true);1293            } else {1294                <ContractWhiteListEnabled<T>>::remove(contract_address);1295            }1296            Ok(())1297        }1298        1299        /// Add an address to smart contract white list.1300        /// 1301        /// # Permissions1302        /// 1303        /// * Address that deployed smart contract.1304        /// 1305        /// # Arguments1306        /// 1307        /// -`contract_address`: Address of the contract.1308        ///1309        /// -`account_address`: Address to add.1310        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1311        #[transactional]1312        pub fn add_to_contract_white_list(1313            origin,1314            contract_address: T::AccountId,1315            account_address: T::AccountId1316        ) -> DispatchResult {1317            let sender = ensure_signed(origin)?;13181319            #[cfg(feature = "runtime-benchmarks")]1320            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1321            1322            Self::ensure_contract_owned(sender, &contract_address)?;      1323            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1324            Ok(())1325        }13261327        /// Remove an address from smart contract white list.1328        /// 1329        /// # Permissions1330        /// 1331        /// * Address that deployed smart contract.1332        /// 1333        /// # Arguments1334        /// 1335        /// -`contract_address`: Address of the contract.1336        ///1337        /// -`account_address`: Address to remove.1338        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1339        #[transactional]1340        pub fn remove_from_contract_white_list(1341            origin,1342            contract_address: T::AccountId,1343            account_address: T::AccountId1344        ) -> DispatchResult {1345            let sender = ensure_signed(origin)?;13461347            #[cfg(feature = "runtime-benchmarks")]1348            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13491350            Self::ensure_contract_owned(sender, &contract_address)?;1351            <ContractWhiteList<T>>::remove(contract_address, account_address);1352            Ok(())1353        }13541355        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1356        #[transactional]1357        pub fn set_collection_limits(1358            origin,1359            collection_id: u32,1360            new_limits: CollectionLimits<T::BlockNumber>,1361        ) -> DispatchResult {1362            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1363            let mut target_collection = Self::get_collection(collection_id)?;1364            Self::check_owner_permissions(&target_collection, &sender.as_sub())?;1365            let old_limits = &target_collection.limits;1366            let chain_limits = ChainLimit::get();13671368            // collection bounds1369            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1370                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1371                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1372                Error::<T>::CollectionLimitBoundsExceeded);13731374            // token_limit   check  prev1375            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1376            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);13771378            ensure!(1379                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1380                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1381                Error::<T>::OwnerPermissionsCantBeReverted,1382            );13831384            target_collection.limits = new_limits;1385            Self::save_collection(target_collection);13861387            Ok(())1388        } 1389    }1390}13911392impl<T: Config> Module<T> {1393    pub fn create_item_internal(sender: &T::CrossAccountId, collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {1394        Self::can_create_items_in_collection(&collection, &sender, &owner, 1)?;1395        Self::validate_create_item_args(&collection, &data)?;1396        Self::create_item_no_validation(&collection, owner, data)?;13971398        Ok(())1399    }14001401    pub fn transfer_internal(sender: &T::CrossAccountId, recipient: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1402        target_collection.consume_gas(2000000)?;1403        // Limits check1404        Self::is_correct_transfer(target_collection, &recipient)?;14051406        // Transfer permissions check1407        ensure!(Self::is_item_owner(&sender, target_collection, item_id) ||1408            Self::is_owner_or_admin_permissions(target_collection, &sender),1409            Error::<T>::NoPermission);14101411        if target_collection.access == AccessMode::WhiteList {1412            Self::check_white_list(target_collection, &sender)?;1413            Self::check_white_list(target_collection, &recipient)?;1414        }14151416        match target_collection.mode1417        {1418            CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1419            CollectionMode::Fungible(_)  => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1420            CollectionMode::ReFungible  => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1421            _ => ()1422        };14231424        Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender.clone(), recipient.clone(), value));14251426        Ok(())1427    }14281429	pub fn approve_internal(1430		sender: &T::CrossAccountId,1431		spender: &T::CrossAccountId,1432		collection: &CollectionHandle<T>,1433		item_id: TokenId,1434		amount: u1281435	) -> DispatchResult {1436        collection.consume_gas(2000000)?;1437		Self::token_exists(&collection, item_id)?;14381439		// Transfer permissions check1440		let bypasses_limits = collection.limits.owner_can_transfer &&1441			Self::is_owner_or_admin_permissions(1442				&collection,1443				&sender,1444			);14451446		let allowance_limit = if bypasses_limits {1447			None1448		} else if let Some(amount) = Self::owned_amount(1449			&sender,1450			&collection,1451			item_id,1452		) {1453			Some(amount)1454		} else {1455			fail!(Error::<T>::NoPermission);1456		};14571458		if collection.access == AccessMode::WhiteList {1459			Self::check_white_list(&collection, &sender)?;1460			Self::check_white_list(&collection, &spender)?;1461		}14621463		let allowance: u128 = amount1464			.checked_add(<Allowances<T>>::get(collection.id, (item_id, sender.as_sub(), spender.as_sub())))1465			.ok_or(Error::<T>::NumOverflow)?;1466		if let Some(limit) = allowance_limit {1467			ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1468		}1469		<Allowances<T>>::insert(collection.id, (item_id, sender.as_sub(), spender.as_sub()), allowance);14701471		if matches!(collection.mode, CollectionMode::NFT) {1472			// TODO: NFT: only one owner may exist for token in ERC7211473			collection.log(ERC721Events::Approval {1474                owner: *sender.as_eth(),1475                approved: *spender.as_eth(),1476                token_id: item_id.into(),1477            });1478		}14791480		if matches!(collection.mode, CollectionMode::Fungible(_)) {1481			// TODO: NFT: only one owner may exist for token in ERC201482			collection.log(ERC20Events::Approval {1483                owner: *sender.as_eth(),1484                spender: *spender.as_eth(),1485                value: allowance.into()1486            });1487		}14881489		Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender.clone(), spender.clone(), allowance));1490		Ok(())1491	}14921493	pub fn transfer_from_internal(1494		sender: &T::CrossAccountId,1495		from: &T::CrossAccountId,1496		recipient: &T::CrossAccountId,1497		collection: &CollectionHandle<T>,1498		item_id: TokenId,1499		amount: u128,1500	) -> DispatchResult {1501        collection.consume_gas(2000000)?;1502		// Check approval1503		let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));15041505		// Limits check1506		Self::is_correct_transfer(&collection, &recipient)?;15071508		// Transfer permissions check1509		ensure!(1510			approval >= amount || 1511			(1512				collection.limits.owner_can_transfer &&1513				Self::is_owner_or_admin_permissions(&collection, &sender)1514			),1515			Error::<T>::NoPermission1516		);15171518		if collection.access == AccessMode::WhiteList {1519			Self::check_white_list(&collection, &sender)?;1520			Self::check_white_list(&collection, &recipient)?;1521		}15221523		// Reduce approval by transferred amount or remove if remaining approval drops to 01524		let allowance = approval.saturating_sub(amount);1525		if allowance > 0 {1526			<Allowances<T>>::insert(collection.id, (item_id, from.as_sub(), sender.as_sub()), allowance);1527		} else {1528			<Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1529		}15301531		match collection.mode {1532			CollectionMode::NFT => {1533				Self::transfer_nft(&collection, item_id, from.clone(), recipient.clone())?1534			}1535			CollectionMode::Fungible(_) => {1536				Self::transfer_fungible(&collection, amount, &from, &recipient)?1537			}1538			CollectionMode::ReFungible => {1539				Self::transfer_refungible(&collection, item_id, amount, from.clone(), recipient.clone())?1540			}1541			_ => ()1542		};15431544		if matches!(collection.mode, CollectionMode::Fungible(_)) {1545			collection.log(ERC20Events::Approval {1546                owner: *from.as_eth(),1547                spender: *sender.as_eth(),1548                value: allowance.into()1549            });1550		}15511552		Ok(())1553	}15541555    pub fn set_variable_meta_data_internal(1556        sender: &T::CrossAccountId,1557        collection: &CollectionHandle<T>, 1558        item_id: TokenId,1559        data: Vec<u8>,1560    ) -> DispatchResult {1561        Self::token_exists(&collection, item_id)?;15621563        ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);15641565        // Modify permissions check1566        ensure!(Self::is_item_owner(&sender, &collection, item_id) ||1567            Self::is_owner_or_admin_permissions(&collection, &sender),1568            Error::<T>::NoPermission);15691570        match collection.mode1571        {1572            CollectionMode::NFT => Self::set_nft_variable_data(&collection, item_id, data)?,1573            CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(&collection, item_id, data)?,1574            CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1575            _ => fail!(Error::<T>::UnexpectedCollectionType)1576        };15771578        Ok(())1579    }15801581    pub fn create_multiple_items_internal(1582        sender: &T::CrossAccountId,1583        collection: &CollectionHandle<T>,1584        owner: &T::CrossAccountId,1585        items_data: Vec<CreateItemData>,1586    ) -> DispatchResult {1587        Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;15881589        for data in &items_data {1590            Self::validate_create_item_args(&collection, data)?;1591        }1592        for data in &items_data {1593            Self::create_item_no_validation(&collection, owner, data.clone())?;1594        }15951596        Ok(())1597    }15981599    pub fn burn_item_internal(1600        sender: &T::CrossAccountId,1601        collection: &CollectionHandle<T>,1602        item_id: TokenId,1603        value: u128,1604    ) -> DispatchResult {1605        ensure!(1606            Self::is_item_owner(&sender, &collection, item_id) ||1607            (1608                collection.limits.owner_can_transfer &&1609                Self::is_owner_or_admin_permissions(&collection, &sender)1610            ),1611            Error::<T>::NoPermission1612        );16131614        if collection.access == AccessMode::WhiteList {1615            Self::check_white_list(&collection, &sender)?;1616        }16171618        match collection.mode1619        {1620            CollectionMode::NFT => Self::burn_nft_item(&collection, item_id)?,1621            CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, &collection, value)?,1622            CollectionMode::ReFungible  => Self::burn_refungible_item(&collection, item_id, &sender)?,1623            _ => ()1624        };16251626        Ok(())1627    }16281629    pub fn toggle_white_list_internal(1630        sender: &T::CrossAccountId,1631        collection: &CollectionHandle<T>,1632        address: &T::CrossAccountId,1633        whitelisted: bool,1634    ) -> DispatchResult {1635        Self::check_owner_or_admin_permissions(&collection, &sender)?;16361637        if whitelisted {1638            <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1639        } else {1640            <WhiteList<T>>::remove(collection.id, address.as_sub());1641        }16421643        Ok(())1644    }16451646    fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::CrossAccountId) -> DispatchResult {1647        let collection_id = collection.id;16481649        // check token limit and account token limit1650        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1651        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1652        1653        Ok(())1654    }16551656    fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::CrossAccountId, owner: &T::CrossAccountId, amount: u32) -> DispatchResult {1657        let collection_id = collection.id;16581659        // check token limit and account token limit1660        let total_items: u32 = ItemListIndex::get(collection_id)1661            .checked_add(amount)1662            .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1663        let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len() as u32)1664            .checked_add(amount)1665            .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1666        ensure!(collection.limits.token_limit >= total_items,  Error::<T>::CollectionTokenLimitExceeded);1667        ensure!(collection.limits.account_token_ownership_limit >= account_items,  Error::<T>::AccountTokenLimitExceeded);16681669        if !Self::is_owner_or_admin_permissions(collection, &sender) {1670            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1671            Self::check_white_list(collection, owner)?;1672            Self::check_white_list(collection, sender)?;1673        }16741675        Ok(())1676    }16771678    fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1679        match target_collection.mode1680        {1681            CollectionMode::NFT => {1682                if let CreateItemData::NFT(data) = data {1683                    // check sizes1684                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1685                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1686                } else {1687                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1688                }1689            },1690            CollectionMode::Fungible(_) => {1691                if let CreateItemData::Fungible(_) = data {1692                } else {1693                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1694                }1695            },1696            CollectionMode::ReFungible => {1697                if let CreateItemData::ReFungible(data) = data {16981699                    // check sizes1700                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1701                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);17021703                    // Check refungibility limits1704                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1705                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1706                } else {1707                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1708                }1709            },1710            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1711        };17121713        Ok(())1714    }17151716    fn create_item_no_validation(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {1717        match data1718        {1719            CreateItemData::NFT(data) => {1720                let item = NftItemType {1721                    owner: owner.clone(),1722                    const_data: data.const_data,1723                    variable_data: data.variable_data1724                };17251726                Self::add_nft_item(collection, item)?;1727            },1728            CreateItemData::Fungible(data) => {1729                Self::add_fungible_item(collection, &owner, data.value)?;1730            },1731            CreateItemData::ReFungible(data) => {1732                let mut owner_list = Vec::new();1733                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});17341735                let item = ReFungibleItemType {1736                    owner: owner_list,1737                    const_data: data.const_data,1738                    variable_data: data.variable_data1739                };17401741                Self::add_refungible_item(collection, item)?;1742            }1743        };17441745        Ok(())1746    }17471748    fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, value: u128) -> DispatchResult {1749        let collection_id = collection.id;17501751        // Does new owner already have an account?1752        let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;17531754        // Mint 1755        let item = FungibleItemType {1756            value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1757        };1758        <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);17591760        // Update balance1761        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1762            .checked_add(value)1763            .ok_or(Error::<T>::NumOverflow)?;1764        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17651766        Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1767        Ok(())1768    }17691770    fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::CrossAccountId>) -> DispatchResult {1771        let collection_id = collection.id;17721773        let current_index = <ItemListIndex>::get(collection_id)1774            .checked_add(1)1775            .ok_or(Error::<T>::NumOverflow)?;1776        let itemcopy = item.clone();17771778        ensure!(1779            item.owner.len() == 1,1780            Error::<T>::BadCreateRefungibleCall,1781        );1782        let item_owner = item.owner.first().expect("only one owner is defined");17831784        let value = item_owner.fraction;1785        let owner = item_owner.owner.clone();17861787        Self::add_token_index(collection_id, current_index, &owner)?;17881789        <ItemListIndex>::insert(collection_id, current_index);1790        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17911792        // Update balance1793        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1794            .checked_add(value)1795            .ok_or(Error::<T>::NumOverflow)?;1796        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17971798        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1799        Ok(())1800    }18011802    fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::CrossAccountId>) -> DispatchResult {1803        let collection_id = collection.id;18041805        let current_index = <ItemListIndex>::get(collection_id)1806            .checked_add(1)1807            .ok_or(Error::<T>::NumOverflow)?;18081809        let item_owner = item.owner.clone();1810        Self::add_token_index(collection_id, current_index, &item.owner)?;18111812        <ItemListIndex>::insert(collection_id, current_index);1813        <NftItemList<T>>::insert(collection_id, current_index, item);18141815        // Update balance1816        let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1817            .checked_add(1)1818            .ok_or(Error::<T>::NumOverflow)?;1819        <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);18201821        collection.log(ERC721Events::Transfer {1822            from: H160::default(),1823            to: *item_owner.as_eth(),1824            token_id: current_index.into(),1825        });1826        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));1827        Ok(())1828    }18291830    fn burn_refungible_item(1831        collection: &CollectionHandle<T>,1832        item_id: TokenId,1833        owner: &T::CrossAccountId,1834    ) -> DispatchResult {1835        let collection_id = collection.id;18361837        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1838            .ok_or(Error::<T>::TokenNotFound)?;1839        let rft_balance = token1840            .owner1841            .iter()1842            .find(|&i| i.owner == *owner)1843            .ok_or(Error::<T>::TokenNotFound)?;1844        Self::remove_token_index(collection_id, item_id, owner)?;18451846        // update balance1847        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1848            .checked_sub(rft_balance.fraction)1849            .ok_or(Error::<T>::NumOverflow)?;1850        <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);18511852        // Re-create owners list with sender removed1853        let index = token1854            .owner1855            .iter()1856            .position(|i| i.owner == *owner)1857            .expect("owned item is exists");1858        token.owner.remove(index);1859        let owner_count = token.owner.len();18601861        // Burn the token completely if this was the last (only) owner1862        if owner_count == 0 {1863            <ReFungibleItemList<T>>::remove(collection_id, item_id);1864            <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1865        }1866        else {1867            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1868        }18691870        Ok(())1871    }18721873    fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1874        let collection_id = collection.id;18751876        let item = <NftItemList<T>>::get(collection_id, item_id)1877            .ok_or(Error::<T>::TokenNotFound)?;1878        Self::remove_token_index(collection_id, item_id, &item.owner)?;18791880        // update balance1881        let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1882            .checked_sub(1)1883            .ok_or(Error::<T>::NumOverflow)?;1884        <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1885        <NftItemList<T>>::remove(collection_id, item_id);1886        <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18871888        Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1889        Ok(())1890    }18911892    fn burn_fungible_item(owner: &T::CrossAccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {1893        let collection_id = collection.id;18941895        let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1896        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18971898        // update balance1899        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1900            .checked_sub(value)1901            .ok_or(Error::<T>::NumOverflow)?;1902        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);19031904        if balance.value - value > 0 {1905            balance.value -= value;1906            <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1907        }1908        else {1909            <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1910        }19111912        collection.log(ERC20Events::Transfer {1913            from: *owner.as_eth(),1914            to: H160::default(),1915            value: value.into(),1916        });1917        Ok(())1918    }19191920    pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1921        Ok(<CollectionHandle<T>>::get(collection_id)1922            .ok_or(Error::<T>::CollectionNotFound)?)1923    }19241925    fn save_collection(collection: CollectionHandle<T>) {1926        <CollectionById<T>>::insert(collection.id, collection.into_inner());1927    }19281929    pub fn submit_logs(collection: CollectionHandle<T>) -> DispatchResult {1930        if collection.logs.is_empty() {1931            return Ok(())1932        }1933        T::EthereumTransactionSender::submit_logs_transaction(1934            eth::generate_transaction(collection.id, T::EthereumChainId::get()),1935            collection.logs.retrieve_logs(),1936        )1937    }19381939    fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: &T::AccountId) -> DispatchResult {1940        ensure!(1941            *subject == target_collection.owner,1942            Error::<T>::NoPermission1943        );19441945        Ok(())1946    }19471948    fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> bool {1949        *subject.as_sub() == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)1950    }19511952    fn check_owner_or_admin_permissions(1953        collection: &CollectionHandle<T>,1954        subject: &T::CrossAccountId,1955    ) -> DispatchResult {1956        ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);19571958        Ok(())1959    }19601961    fn owned_amount(1962        subject: &T::CrossAccountId,1963        target_collection: &CollectionHandle<T>,1964        item_id: TokenId,1965    ) -> Option<u128> {1966        let collection_id = target_collection.id;19671968        match target_collection.mode {1969            CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject)1970                .then(|| 1),1971            CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub())1972                .value),1973            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1974                .owner1975                .iter()1976                .find(|i| i.owner == *subject)1977                .map(|i| i.fraction),1978            CollectionMode::Invalid => None,1979        }1980    }19811982    fn is_item_owner(subject: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {1983        match target_collection.mode {1984            CollectionMode::Fungible(_) => true,1985            _ => Self::owned_amount(&subject, target_collection, item_id).is_some(),1986        }1987    }19881989    fn check_white_list(collection: &CollectionHandle<T>, address: &T::CrossAccountId) -> DispatchResult {1990        let collection_id = collection.id;19911992        let mes = Error::<T>::AddresNotInWhiteList;1993        ensure!(<WhiteList<T>>::contains_key(collection_id, address.as_sub()), mes);19941995        Ok(())1996    }19971998    /// Check if token exists. In case of Fungible, check if there is an entry for 1999    /// the owner in fungible balances double map2000    fn token_exists(2001        target_collection: &CollectionHandle<T>,2002        item_id: TokenId,2003    ) -> DispatchResult {2004        let collection_id = target_collection.id;2005        let exists = match target_collection.mode2006        {2007            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2008            CollectionMode::Fungible(_)  => true,2009            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2010            _ => false2011        };20122013        ensure!(exists == true, Error::<T>::TokenNotFound);2014        Ok(())2015    }20162017    fn transfer_fungible(2018        collection: &CollectionHandle<T>,2019        value: u128,2020        owner: &T::CrossAccountId,2021        recipient: &T::CrossAccountId,2022    ) -> DispatchResult {2023        let collection_id = collection.id;20242025        let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2026        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);20272028        // Send balance to recipient (updates balanceOf of recipient)2029        Self::add_fungible_item(collection, recipient, value)?;20302031        // update balanceOf of sender2032        <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);20332034        // Reduce or remove sender2035        if balance.value == value {2036            <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2037        }2038        else {2039            balance.value -= value;2040            <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2041        }20422043        collection.log(ERC20Events::Transfer {2044            from: *owner.as_eth(),2045            to: *recipient.as_eth(),2046            value: value.into(),2047        });2048        Self::deposit_event(RawEvent::Transfer(collection.id, 1, owner.clone(), recipient.clone(), value));20492050        Ok(())2051    }20522053    fn transfer_refungible(2054        collection: &CollectionHandle<T>,2055        item_id: TokenId,2056        value: u128,2057        owner: T::CrossAccountId,2058        new_owner: T::CrossAccountId,2059    ) -> DispatchResult {2060        let collection_id = collection.id;2061        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2062            .ok_or(Error::<T>::TokenNotFound)?;20632064        let item = full_item2065            .owner2066            .iter()2067            .filter(|i| i.owner == owner)2068            .next()2069            .ok_or(Error::<T>::TokenNotFound)?;2070        let amount = item.fraction;20712072        ensure!(amount >= value, Error::<T>::TokenValueTooLow);20732074        // update balance2075        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2076            .checked_sub(value)2077            .ok_or(Error::<T>::NumOverflow)?;2078        <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);20792080        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2081            .checked_add(value)2082            .ok_or(Error::<T>::NumOverflow)?;2083        <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20842085        let old_owner = item.owner.clone();2086        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20872088        // transfer2089        if amount == value && !new_owner_has_account {2090            // change owner2091            // new owner do not have account2092            let mut new_full_item = full_item.clone();2093            new_full_item2094                .owner2095                .iter_mut()2096                .find(|i| i.owner == owner)2097                .expect("old owner does present in refungible")2098                .owner = new_owner.clone();2099            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);21002101            // update index collection2102            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2103        } else {2104            let mut new_full_item = full_item.clone();2105            new_full_item2106                .owner2107                .iter_mut()2108                .find(|i| i.owner == owner)2109                .expect("old owner does present in refungible")2110                .fraction -= value;21112112            // separate amount2113            if new_owner_has_account {2114                // new owner has account2115                new_full_item2116                    .owner2117                    .iter_mut()2118                    .find(|i| i.owner == new_owner)2119                    .expect("new owner has account")2120                    .fraction += value;2121            } else {2122                // new owner do not have account2123                new_full_item.owner.push(Ownership {2124                    owner: new_owner.clone(),2125                    fraction: value,2126                });2127                Self::add_token_index(collection_id, item_id, &new_owner)?;2128            }21292130            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2131        }21322133        Self::deposit_event(RawEvent::Transfer(collection.id, item_id, owner, new_owner, amount));21342135        Ok(())2136    }21372138    fn transfer_nft(2139        collection: &CollectionHandle<T>,2140        item_id: TokenId,2141        sender: T::CrossAccountId,2142        new_owner: T::CrossAccountId,2143    ) -> DispatchResult {2144        let collection_id = collection.id;2145        let mut item = <NftItemList<T>>::get(collection_id, item_id)2146            .ok_or(Error::<T>::TokenNotFound)?;21472148        ensure!(2149            sender == item.owner,2150            Error::<T>::MustBeTokenOwner2151        );21522153        // update balance2154        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2155            .checked_sub(1)2156            .ok_or(Error::<T>::NumOverflow)?;2157        <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21582159        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2160            .checked_add(1)2161            .ok_or(Error::<T>::NumOverflow)?;2162        <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21632164        // change owner2165        let old_owner = item.owner.clone();2166        item.owner = new_owner.clone();2167        <NftItemList<T>>::insert(collection_id, item_id, item);21682169        // update index collection2170        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21712172        collection.log(ERC721Events::Transfer {2173            from: *sender.as_eth(),2174            to: *new_owner.as_eth(),2175            token_id: item_id.into(),2176        });2177        Self::deposit_event(RawEvent::Transfer(collection.id, item_id, sender, new_owner, 1));21782179        Ok(())2180    }2181    2182    fn set_re_fungible_variable_data(2183        collection: &CollectionHandle<T>,2184        item_id: TokenId,2185        data: Vec<u8>2186    ) -> DispatchResult {2187        let collection_id = collection.id;2188        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2189            .ok_or(Error::<T>::TokenNotFound)?;21902191        item.variable_data = data;21922193        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21942195        Ok(())2196    }21972198    fn set_nft_variable_data(2199        collection: &CollectionHandle<T>,2200        item_id: TokenId,2201        data: Vec<u8>2202    ) -> DispatchResult {2203        let collection_id = collection.id;2204        let mut item = <NftItemList<T>>::get(collection_id, item_id)2205            .ok_or(Error::<T>::TokenNotFound)?;2206        2207        item.variable_data = data;22082209        <NftItemList<T>>::insert(collection_id, item_id, item);2210        2211        Ok(())2212    }22132214    #[allow(dead_code)]2215    fn init_collection(item: &Collection<T>) {2216        // check params2217        assert!(2218            item.decimal_points <= MAX_DECIMAL_POINTS,2219            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2220        );2221        assert!(2222            item.name.len() <= 64,2223            "Collection name can not be longer than 63 char"2224        );2225        assert!(2226            item.name.len() <= 256,2227            "Collection description can not be longer than 255 char"2228        );2229        assert!(2230            item.token_prefix.len() <= 16,2231            "Token prefix can not be longer than 15 char"2232        );22332234        // Generate next collection ID2235        let next_id = CreatedCollectionCount::get()2236            .checked_add(1)2237            .unwrap();22382239        CreatedCollectionCount::put(next_id);2240    }22412242    #[allow(dead_code)]2243    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2244        let current_index = <ItemListIndex>::get(collection_id)2245            .checked_add(1)2246            .unwrap();22472248        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22492250        <ItemListIndex>::insert(collection_id, current_index);22512252        // Update balance2253        let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2254            .checked_add(1)2255            .unwrap();2256        <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2257    }22582259    #[allow(dead_code)]2260    fn init_fungible_token(collection_id: CollectionId, owner: &T::CrossAccountId, item: &FungibleItemType) {2261        let current_index = <ItemListIndex>::get(collection_id)2262            .checked_add(1)2263            .unwrap();22642265        Self::add_token_index(collection_id, current_index, owner).unwrap();22662267        <ItemListIndex>::insert(collection_id, current_index);22682269        // Update balance2270        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2271            .checked_add(item.value)2272            .unwrap();2273        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2274    }22752276    #[allow(dead_code)]2277    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::CrossAccountId>) {2278        let current_index = <ItemListIndex>::get(collection_id)2279            .checked_add(1)2280            .unwrap();22812282        let value = item.owner.first().unwrap().fraction;2283        let owner = item.owner.first().unwrap().owner.clone();22842285        Self::add_token_index(collection_id, current_index, &owner).unwrap();22862287        <ItemListIndex>::insert(collection_id, current_index);22882289        // Update balance2290        let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2291            .checked_add(value)2292            .unwrap();2293        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2294    }22952296    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::CrossAccountId) -> DispatchResult {2297        // add to account limit2298        if <AccountItemCount<T>>::contains_key(owner.as_sub()) {22992300            // bound Owned tokens by a single address2301            let count = <AccountItemCount<T>>::get(owner.as_sub());2302            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);23032304            <AccountItemCount<T>>::insert(owner.as_sub(), count2305                .checked_add(1)2306                .ok_or(Error::<T>::NumOverflow)?);2307        }2308        else {2309            <AccountItemCount<T>>::insert(owner.as_sub(), 1);2310        }23112312        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2313        if list_exists {2314            let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2315            let item_contains = list.contains(&item_index.clone());23162317            if !item_contains {2318                list.push(item_index.clone());2319            }23202321            <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2322        } else {2323            let mut itm = Vec::new();2324            itm.push(item_index.clone());2325            <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2326        }23272328        Ok(())2329    }23302331    fn remove_token_index(2332        collection_id: CollectionId,2333        item_index: TokenId,2334        owner: &T::CrossAccountId,2335    ) -> DispatchResult {23362337        // update counter2338        <AccountItemCount<T>>::insert(owner.as_sub(), 2339            <AccountItemCount<T>>::get(owner.as_sub())2340            .checked_sub(1)2341            .ok_or(Error::<T>::NumOverflow)?);234223432344        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2345        if list_exists {2346            let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2347            let item_contains = list.contains(&item_index.clone());23482349            if item_contains {2350                list.retain(|&item| item != item_index);2351                <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2352            }2353        }23542355        Ok(())2356    }23572358    fn move_token_index(2359        collection_id: CollectionId,2360        item_index: TokenId,2361        old_owner: &T::CrossAccountId,2362        new_owner: &T::CrossAccountId,2363    ) -> DispatchResult {2364        Self::remove_token_index(collection_id, item_index, old_owner)?;2365        Self::add_token_index(collection_id, item_index, new_owner)?;23662367        Ok(())2368    }2369    2370    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2371        ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);23722373        Ok(())2374    }2375}