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

difftreelog

source

pallets/nft/src/lib.rs82.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)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516pub use frame_support::{17    construct_runtime, decl_event, decl_module, decl_storage, decl_error,18    dispatch::DispatchResult,19    ensure, fail, parameter_types,20    traits::{21        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,22        Randomness, IsSubType, WithdrawReasons,23    },24    weights::{25        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},26        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,27        WeightToFeePolynomial, DispatchClass,28    },29    StorageValue,30    transactional,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use 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, CollectionHandle, TokenId, 39    SchemaVersion, SponsorshipState, Ownership,40    NftItemType, FungibleItemType, ReFungibleItemType41};4243#[cfg(test)]44mod mock;4546#[cfg(test)]47mod tests;4849mod default_weights;5051#[cfg(feature = "runtime-benchmarks")]52mod benchmarking;5354pub trait WeightInfo {55	fn create_collection() -> Weight;56	fn destroy_collection() -> Weight;57	fn add_to_white_list() -> Weight;58	fn remove_from_white_list() -> Weight;59    fn set_public_access_mode() -> Weight;60    fn set_mint_permission() -> Weight;61    fn change_collection_owner() -> Weight;62    fn add_collection_admin() -> Weight;63    fn remove_collection_admin() -> Weight;64    fn set_collection_sponsor() -> Weight;65    fn confirm_sponsorship() -> Weight;66    fn remove_collection_sponsor() -> Weight;67    fn create_item(s: usize) -> Weight;68    fn burn_item() -> Weight;69    fn transfer() -> Weight;70    fn approve() -> Weight;71    fn transfer_from() -> Weight;72    fn set_offchain_schema() -> Weight;73    fn set_const_on_chain_schema() -> Weight;74    fn set_variable_on_chain_schema() -> Weight;75    fn set_variable_meta_data() -> Weight;76    fn enable_contract_sponsoring() -> Weight;77    fn set_schema_version() -> Weight;78    fn set_chain_limits() -> Weight;79    fn set_contract_sponsoring_rate_limit() -> Weight;80    fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;81    fn toggle_contract_white_list() -> Weight;82    fn add_to_contract_white_list() -> Weight;83    fn remove_from_contract_white_list() -> Weight;84    fn set_collection_limits() -> Weight;85}8687decl_error! {88	/// Error for non-fungible-token module.89	pub enum Error for Module<T: Config> {90        /// Total collections bound exceeded.91        TotalCollectionsLimitExceeded,92		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.93        CollectionDecimalPointLimitExceeded, 94        /// Collection name can not be longer than 63 char.95        CollectionNameLimitExceeded, 96        /// Collection description can not be longer than 255 char.97        CollectionDescriptionLimitExceeded, 98        /// Token prefix can not be longer than 15 char.99        CollectionTokenPrefixLimitExceeded,100        /// This collection does not exist.101        CollectionNotFound,102        /// Item not exists.103        TokenNotFound,104        /// Admin not found105        AdminNotFound,106        /// Arithmetic calculation overflow.107        NumOverflow,       108        /// Account already has admin role.109        AlreadyAdmin,  110        /// You do not own this collection.111        NoPermission,112        /// This address is not set as sponsor, use setCollectionSponsor first.113        ConfirmUnsetSponsorFail,114        /// Collection is not in mint mode.115        PublicMintingNotAllowed,116        /// Sender parameter and item owner must be equal.117        MustBeTokenOwner,118        /// Item balance not enough.119        TokenValueTooLow,120        /// Size of item is too large.121        NftSizeLimitExceeded,122        /// No approve found123        ApproveNotFound,124        /// Requested value more than approved.125        TokenValueNotEnough,126        /// Only approved addresses can call this method.127        ApproveRequired,128        /// Address is not in white list.129        AddresNotInWhiteList,130        /// Number of collection admins bound exceeded.131        CollectionAdminsLimitExceeded,132        /// Owned tokens by a single address bound exceeded.133        AddressOwnershipLimitExceeded,134        /// Length of items properties must be greater than 0.135        EmptyArgument,136        /// const_data exceeded data limit.137        TokenConstDataLimitExceeded,138        /// variable_data exceeded data limit.139        TokenVariableDataLimitExceeded,140        /// Not NFT item data used to mint in NFT collection.141        NotNftDataUsedToMintNftCollectionToken,142        /// Not Fungible item data used to mint in Fungible collection.143        NotFungibleDataUsedToMintFungibleCollectionToken,144        /// Not Re Fungible item data used to mint in Re Fungible collection.145        NotReFungibleDataUsedToMintReFungibleCollectionToken,146        /// Unexpected collection type.147        UnexpectedCollectionType,148        /// Can't store metadata in fungible tokens.149        CantStoreMetadataInFungibleTokens,150        /// Collection token limit exceeded151        CollectionTokenLimitExceeded,152        /// Account token limit exceeded per collection153        AccountTokenLimitExceeded,154        /// Collection limit bounds per collection exceeded155        CollectionLimitBoundsExceeded,156        /// Tried to enable permissions which are only permitted to be disabled157        OwnerPermissionsCantBeReverted,158        /// Schema data size limit bound exceeded159        SchemaDataLimitExceeded,160        /// Maximum refungibility exceeded161        WrongRefungiblePieces,162        /// createRefungible should be called with one owner163        BadCreateRefungibleCall,164	}165}166167// + pallet_transaction_payment::Config  + pallet_nft_transaction_payment::Config + pallet_contracts::Config168169170pub trait Config: system::Config + Sized {171    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;172173    /// Weight information for extrinsics in this pallet.174	type WeightInfo: WeightInfo;175176    type Currency: Currency<Self::AccountId>;177    type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;178    type TreasuryAccountId: Get<Self::AccountId>;179}180181// # Used definitions182//183// ## User control levels184//185// chain-controlled - key is uncontrolled by user186//                    i.e autoincrementing index187//                    can use non-cryptographic hash188// real - key is controlled by user189//        but it is hard to generate enough colliding values, i.e owner of signed txs190//        can use non-cryptographic hash191// controlled - key is completly controlled by users192//              i.e maps with mutable keys193//              should use cryptographic hash194//195// ## User control level downgrade reasons196//197// ?1 - chain-controlled -> controlled198//      collections/tokens can be destroyed, resulting in massive holes199// ?2 - chain-controlled -> controlled200//      same as ?1, but can be only added, resulting in easier exploitation201// ?3 - real -> controlled202//      no confirmation required, so addresses can be easily generated203decl_storage! {204    trait Store for Module<T: Config> as Nft {205206        //#region Private members207        /// Id of next collection208        CreatedCollectionCount: u32;209        /// Used for migrations210        ChainVersion: u64;211        /// Id of last collection token212        /// Collection id (controlled?1)213        ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;214        //#endregion215216        //#region Chain limits struct217        pub ChainLimit get(fn chain_limit) config(): ChainLimits;218        //#endregion219220        //#region Bound counters221        /// Amount of collections destroyed, used for total amount tracking with222        /// CreatedCollectionCount223        DestroyedCollectionCount: u32;224        /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)225        /// Account id (real)226        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;227        //#endregion228229        //#region Basic collections230        /// Collection info231        /// Collection id (controlled?1)232        pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;233        /// List of collection admins234        /// Collection id (controlled?2)235        pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::AccountId>;236        /// Whitelisted collection users237        /// Collection id (controlled?2), user id (controlled?3)238        pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;239        //#endregion240241        /// How many of collection items user have242        /// Collection id (controlled?2), account id (real)243        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;244245        /// Amount of items which spender can transfer out of owners account (via transferFrom)246        /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))247        /// TODO: Off chain worker should remove from this map when token gets removed248        pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;249250        //#region Item collections251        /// Collection id (controlled?2), token id (controlled?1)252        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::AccountId>>;253        /// Collection id (controlled?2), owner (controlled?2)254        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;255        /// Collection id (controlled?2), token id (controlled?1)256        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::AccountId>>;257        //#endregion258259        //#region Index list260        /// Collection id (controlled?2), tokens owner (controlled?2)261        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;262        //#endregion263264        //#region Tokens transfer rate limit baskets265        /// (Collection id (controlled?2), who created (real))266        /// TODO: Off chain worker should remove from this map when collection gets removed267        pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;268        /// Collection id (controlled?2), token id (controlled?2)269        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;270        /// Collection id (controlled?2), owning user (real)271        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;272        /// Collection id (controlled?2), token id (controlled?2)273        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;274        //#endregion275276        /// Variable metadata sponsoring277        /// Collection id (controlled?2), token id (controlled?2)278        pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;279      280        //#region Contract Sponsorship and Ownership281        /// Contract address (real)282        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;283        /// Contract address (real)284        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;285        /// (Contract address(real), caller (real))286        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;287        /// Contract address (real)288        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;289        /// Contract address (real)290        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 291        /// Contract address (real) => Whitelisted user (controlled?3)292        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 293        //#endregion294    }295    add_extra_genesis {296        build(|config: &GenesisConfig<T>| {297            // Modification of storage298            for (_num, _c) in &config.collection_id {299                <Module<T>>::init_collection(_c);300            }301302            for (_num, _c, _i) in &config.nft_item_id {303                <Module<T>>::init_nft_token(*_c, _i);304            }305306            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {307                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);308            }309310            for (_num, _c, _i) in &config.refungible_item_id {311                <Module<T>>::init_refungible_token(*_c, _i);312            }313        })314    }315}316317decl_event!(318    pub enum Event<T>319    where320        AccountId = <T as system::Config>::AccountId,321    {322        /// New collection was created323        /// 324        /// # Arguments325        /// 326        /// * collection_id: Globally unique identifier of newly created collection.327        /// 328        /// * mode: [CollectionMode] converted into u8.329        /// 330        /// * account_id: Collection owner.331        CollectionCreated(CollectionId, u8, AccountId),332333        /// New item was created.334        /// 335        /// # Arguments336        /// 337        /// * collection_id: Id of the collection where item was created.338        /// 339        /// * item_id: Id of an item. Unique within the collection.340        ///341        /// * recipient: Owner of newly created item 342        ItemCreated(CollectionId, TokenId, AccountId),343344        /// Collection item was burned.345        /// 346        /// # Arguments347        /// 348        /// collection_id.349        /// 350        /// item_id: Identifier of burned NFT.351        ItemDestroyed(CollectionId, TokenId),352353        /// Item was transferred354        ///355        /// * collection_id: Id of collection to which item is belong356        ///357        /// * item_id: Id of an item358        ///359        /// * sender: Original owner of item360        ///361        /// * recipient: New owner of item362        ///363        /// * amount: Always 1 for NFT364        Transfer(CollectionId, TokenId, AccountId, AccountId, u128),365366        /// * collection_id367        ///368        /// * item_id369        ///370        /// * sender371        ///372        /// * spender373        ///374        /// * amount375        Approved(CollectionId, TokenId, AccountId, AccountId, u128),376    }377);378379decl_module! {380    pub struct Module<T: Config> for enum Call 381    where 382        origin: T::Origin383    {384        fn deposit_event() = default;385        type Error = Error<T>;386387        fn on_initialize(_now: T::BlockNumber) -> Weight {388            0389        }390391        /// 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.392        /// 393        /// # Permissions394        /// 395        /// * Anyone.396        /// 397        /// # Arguments398        /// 399        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.400        /// 401        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.402        /// 403        /// * token_prefix: UTF-8 string with token prefix.404        /// 405        /// * mode: [CollectionMode] collection type and type dependent data.406        // returns collection ID407        #[weight = <T as Config>::WeightInfo::create_collection()]408        #[transactional]409        pub fn create_collection(origin,410                                 collection_name: Vec<u16>,411                                 collection_description: Vec<u16>,412                                 token_prefix: Vec<u8>,413                                 mode: CollectionMode) -> DispatchResult {414415            // Anyone can create a collection416            let who = ensure_signed(origin)?;417418            // Take a (non-refundable) deposit of collection creation419            let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();420            imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(421                &T::TreasuryAccountId::get(),422                T::CollectionCreationPrice::get(),423            ));424            <T as Config>::Currency::settle(425                &who,426                imbalance,427                WithdrawReasons::TRANSFER,428                ExistenceRequirement::KeepAlive,429            ).map_err(|_| Error::<T>::NoPermission)?;430431            let decimal_points = match mode {432                CollectionMode::Fungible(points) => points,433                _ => 0434            };435436            let chain_limit = ChainLimit::get();437438            let created_count = CreatedCollectionCount::get();439            let destroyed_count = DestroyedCollectionCount::get();440441            // bound Total number of collections442            ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);443444            // check params445            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);446            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);447            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);448            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);449450            // Generate next collection ID451            let next_id = created_count452                .checked_add(1)453                .ok_or(Error::<T>::NumOverflow)?;454455            CreatedCollectionCount::put(next_id);456457            let limits = CollectionLimits {458                sponsored_data_size: chain_limit.custom_data_limit,459                ..Default::default()460            };461462            // Create new collection463            let new_collection = Collection {464                owner: who.clone(),465                name: collection_name,466                mode: mode.clone(),467                mint_mode: false,468                access: AccessMode::Normal,469                description: collection_description,470                decimal_points: decimal_points,471                token_prefix: token_prefix,472                offchain_schema: Vec::new(),473                schema_version: SchemaVersion::ImageURL,474                sponsorship: SponsorshipState::Disabled,475                variable_on_chain_schema: Vec::new(),476                const_on_chain_schema: Vec::new(),477                limits,478            };479480            // Add new collection to map481            <CollectionById<T>>::insert(next_id, new_collection);482483            // call event484            Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who.clone()));485486            Ok(())487        }488489        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.490        /// 491        /// # Permissions492        /// 493        /// * Collection Owner.494        /// 495        /// # Arguments496        /// 497        /// * collection_id: collection to destroy.498        #[weight = <T as Config>::WeightInfo::destroy_collection()]499        #[transactional]500        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {501502            let sender = ensure_signed(origin)?;503            let collection = Self::get_collection(collection_id)?;504            Self::check_owner_permissions(&collection, sender)?;505            if !collection.limits.owner_can_destroy {506                fail!(Error::<T>::NoPermission);507            }508509            <AddressTokens<T>>::remove_prefix(collection_id);510            <Allowances<T>>::remove_prefix(collection_id);511            <Balance<T>>::remove_prefix(collection_id);512            <ItemListIndex>::remove(collection_id);513            <AdminList<T>>::remove(collection_id);514            <CollectionById<T>>::remove(collection_id);515            <WhiteList<T>>::remove_prefix(collection_id);516517            <NftItemList<T>>::remove_prefix(collection_id);518            <FungibleItemList<T>>::remove_prefix(collection_id);519            <ReFungibleItemList<T>>::remove_prefix(collection_id);520521            <NftTransferBasket<T>>::remove_prefix(collection_id);522            <FungibleTransferBasket<T>>::remove_prefix(collection_id);523            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);524525            <VariableMetaDataBasket<T>>::remove_prefix(collection_id);526527            DestroyedCollectionCount::put(DestroyedCollectionCount::get()528                .checked_add(1)529                .ok_or(Error::<T>::NumOverflow)?);530531            Ok(())532        }533534        /// Add an address to white list.535        /// 536        /// # Permissions537        /// 538        /// * Collection Owner539        /// * Collection Admin540        /// 541        /// # Arguments542        /// 543        /// * collection_id.544        /// 545        /// * address.546        #[weight = <T as Config>::WeightInfo::add_to_white_list()]547        #[transactional]548        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{549550            let sender = ensure_signed(origin)?;551            let collection = Self::get_collection(collection_id)?;552            Self::check_owner_or_admin_permissions(&collection, sender)?;553554            <WhiteList<T>>::insert(collection_id, address, true);555            556            Ok(())557        }558559        /// Remove an address from white list.560        /// 561        /// # Permissions562        /// 563        /// * Collection Owner564        /// * Collection Admin565        /// 566        /// # Arguments567        /// 568        /// * collection_id.569        /// 570        /// * address.571        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]572        #[transactional]573        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{574575            let sender = ensure_signed(origin)?;576            let collection = Self::get_collection(collection_id)?;577            Self::check_owner_or_admin_permissions(&collection, sender)?;578579            <WhiteList<T>>::remove(collection_id, address);580581            Ok(())582        }583584        /// Toggle between normal and white list access for the methods with access for `Anyone`.585        /// 586        /// # Permissions587        /// 588        /// * Collection Owner.589        /// 590        /// # Arguments591        /// 592        /// * collection_id.593        /// 594        /// * mode: [AccessMode]595        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]596        #[transactional]597        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult598        {599            let sender = ensure_signed(origin)?;600601            let mut target_collection = Self::get_collection(collection_id)?;602            Self::check_owner_permissions(&target_collection, sender)?;603            target_collection.access = mode;604            Self::save_collection(target_collection);605606            Ok(())607        }608609        /// Allows Anyone to create tokens if:610        /// * White List is enabled, and611        /// * Address is added to white list, and612        /// * This method was called with True parameter613        /// 614        /// # Permissions615        /// * Collection Owner616        ///617        /// # Arguments618        /// 619        /// * collection_id.620        /// 621        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.622        #[weight = <T as Config>::WeightInfo::set_mint_permission()]623        #[transactional]624        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult625        {626            let sender = ensure_signed(origin)?;627628            let mut target_collection = Self::get_collection(collection_id)?;629            Self::check_owner_permissions(&target_collection, sender)?;630            target_collection.mint_mode = mint_permission;631            Self::save_collection(target_collection);632633            Ok(())634        }635636        /// Change the owner of the collection.637        /// 638        /// # Permissions639        /// 640        /// * Collection Owner.641        /// 642        /// # Arguments643        /// 644        /// * collection_id.645        /// 646        /// * new_owner.647        #[weight = <T as Config>::WeightInfo::change_collection_owner()]648        #[transactional]649        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {650651            let sender = ensure_signed(origin)?;652            let mut target_collection = Self::get_collection(collection_id)?;653            Self::check_owner_permissions(&target_collection, sender)?;654            target_collection.owner = new_owner;655            Self::save_collection(target_collection);656657            Ok(())658        }659660        /// Adds an admin of the Collection.661        /// 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. 662        /// 663        /// # Permissions664        /// 665        /// * Collection Owner.666        /// * Collection Admin.667        /// 668        /// # Arguments669        /// 670        /// * collection_id: ID of the Collection to add admin for.671        /// 672        /// * new_admin_id: Address of new admin to add.673        #[weight = <T as Config>::WeightInfo::add_collection_admin()]674        #[transactional]675        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {676677            let sender = ensure_signed(origin)?;678            let collection = Self::get_collection(collection_id)?;679            Self::check_owner_or_admin_permissions(&collection, sender)?;680            let mut admin_arr = <AdminList<T>>::get(collection_id);681682            match admin_arr.binary_search(&new_admin_id) {683                Ok(_) => {},684                Err(idx) => {685                    let limits = ChainLimit::get();686                    ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);687                    admin_arr.insert(idx, new_admin_id);688                    <AdminList<T>>::insert(collection_id, admin_arr);689                }690            }691            Ok(())692        }693694        /// 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.695        ///696        /// # Permissions697        /// 698        /// * Collection Owner.699        /// * Collection Admin.700        /// 701        /// # Arguments702        /// 703        /// * collection_id: ID of the Collection to remove admin for.704        /// 705        /// * account_id: Address of admin to remove.706        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]707        #[transactional]708        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {709710            let sender = ensure_signed(origin)?;711            let collection = Self::get_collection(collection_id)?;712            Self::check_owner_or_admin_permissions(&collection, sender)?;713            let mut admin_arr = <AdminList<T>>::get(collection_id);714715            match admin_arr.binary_search(&account_id) {716                Ok(idx) => {717                    admin_arr.remove(idx);718                    <AdminList<T>>::insert(collection_id, admin_arr);719                },720                Err(_) => {}721            }722            Ok(())723        }724725        /// # Permissions726        /// 727        /// * Collection Owner728        /// 729        /// # Arguments730        /// 731        /// * collection_id.732        /// 733        /// * new_sponsor.734        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]735        #[transactional]736        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {737738            let sender = ensure_signed(origin)?;739            let mut target_collection = Self::get_collection(collection_id)?;740            Self::check_owner_permissions(&target_collection, sender)?;741742            target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);743            Self::save_collection(target_collection);744745            Ok(())746        }747748        /// # Permissions749        /// 750        /// * Sponsor.751        /// 752        /// # Arguments753        /// 754        /// * collection_id.755        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]756        #[transactional]757        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {758759            let sender = ensure_signed(origin)?;760761            let mut target_collection = Self::get_collection(collection_id)?;762            ensure!(763                target_collection.sponsorship.pending_sponsor() == Some(&sender),764                Error::<T>::ConfirmUnsetSponsorFail765            );766767            target_collection.sponsorship = SponsorshipState::Confirmed(sender);768            Self::save_collection(target_collection);769770            Ok(())771        }772773        /// Switch back to pay-per-own-transaction model.774        ///775        /// # Permissions776        ///777        /// * Collection owner.778        /// 779        /// # Arguments780        /// 781        /// * collection_id.782        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]783        #[transactional]784        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {785786            let sender = ensure_signed(origin)?;787788            let mut target_collection = Self::get_collection(collection_id)?;789            Self::check_owner_permissions(&target_collection, sender)?;790791            target_collection.sponsorship = SponsorshipState::Disabled;792            Self::save_collection(target_collection);793794            Ok(())795        }796797        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.798        /// 799        /// # Permissions800        /// 801        /// * Collection Owner.802        /// * Collection Admin.803        /// * Anyone if804        ///     * White List is enabled, and805        ///     * Address is added to white list, and806        ///     * MintPermission is enabled (see SetMintPermission method)807        /// 808        /// # Arguments809        /// 810        /// * collection_id: ID of the collection.811        /// 812        /// * owner: Address, initial owner of the NFT.813        ///814        /// * data: Token data to store on chain.815        // #[weight =816        // (130_000_000 as Weight)817        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))818        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))819        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]820821        #[weight = <T as Config>::WeightInfo::create_item(data.len())]822        #[transactional]823        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {824            let sender = ensure_signed(origin)?;825            Self::create_item_internal(sender, collection_id, owner, data)826        }827828        /// This method creates multiple items in a collection created with CreateCollection method.829        /// 830        /// # Permissions831        /// 832        /// * Collection Owner.833        /// * Collection Admin.834        /// * Anyone if835        ///     * White List is enabled, and836        ///     * Address is added to white list, and837        ///     * MintPermission is enabled (see SetMintPermission method)838        /// 839        /// # Arguments840        /// 841        /// * collection_id: ID of the collection.842        /// 843        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].844        /// 845        /// * owner: Address, initial owner of the NFT.846        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()847                               .map(|data| { data.len() })848                               .sum())]849        #[transactional]850        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {851852            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);853            let sender = ensure_signed(origin)?;854855            let target_collection = Self::get_collection(collection_id)?;856857            Self::can_create_items_in_collection(&target_collection, &sender, &owner, items_data.len() as u32)?;858859            for data in &items_data {860                Self::validate_create_item_args(&target_collection, data)?;861            }862            for data in &items_data {863                Self::create_item_no_validation(&target_collection, owner.clone(), data.clone())?;864            }865866            Ok(())867        }868869        /// Destroys a concrete instance of NFT.870        /// 871        /// # Permissions872        /// 873        /// * Collection Owner.874        /// * Collection Admin.875        /// * Current NFT Owner.876        /// 877        /// # Arguments878        /// 879        /// * collection_id: ID of the collection.880        /// 881        /// * item_id: ID of NFT to burn.882        #[weight = <T as Config>::WeightInfo::burn_item()]883        #[transactional]884        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {885886            let sender = ensure_signed(origin)?;887888            // Transfer permissions check889            let target_collection = Self::get_collection(collection_id)?;890            ensure!(891                Self::is_item_owner(sender.clone(), &target_collection, item_id) ||892                (893                    target_collection.limits.owner_can_transfer &&894                    Self::is_owner_or_admin_permissions(&target_collection, sender.clone())895                ),896                Error::<T>::NoPermission897            );898899            if target_collection.access == AccessMode::WhiteList {900                Self::check_white_list(&target_collection, &sender)?;901            }902903            match target_collection.mode904            {905                CollectionMode::NFT => Self::burn_nft_item(&target_collection, item_id)?,906                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, &target_collection, value)?,907                CollectionMode::ReFungible  => Self::burn_refungible_item(&target_collection, item_id, &sender)?,908                _ => ()909            };910911            // call event912            Self::deposit_event(RawEvent::ItemDestroyed(target_collection.id, item_id));913914            Ok(())915        }916917        /// Change ownership of the token.918        /// 919        /// # Permissions920        /// 921        /// * Collection Owner922        /// * Collection Admin923        /// * Current NFT owner924        ///925        /// # Arguments926        /// 927        /// * recipient: Address of token recipient.928        /// 929        /// * collection_id.930        /// 931        /// * item_id: ID of the item932        ///     * Non-Fungible Mode: Required.933        ///     * Fungible Mode: Ignored.934        ///     * Re-Fungible Mode: Required.935        /// 936        /// * value: Amount to transfer.937        ///     * Non-Fungible Mode: Ignored938        ///     * Fungible Mode: Must specify transferred amount939        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)940        #[weight = <T as Config>::WeightInfo::transfer()]941        #[transactional]942        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {943            let sender = ensure_signed(origin)?;944            let collection = Self::get_collection(collection_id)?;945946            Self::transfer_internal(sender, recipient, &collection, item_id, value)947        }948949        /// Set, change, or remove approved address to transfer the ownership of the NFT.950        /// 951        /// # Permissions952        /// 953        /// * Collection Owner954        /// * Collection Admin955        /// * Current NFT owner956        /// 957        /// # Arguments958        /// 959        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).960        /// 961        /// * collection_id.962        /// 963        /// * item_id: ID of the item.964        #[weight = <T as Config>::WeightInfo::approve()]965        #[transactional]966        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {967968            let sender = ensure_signed(origin)?;969            let target_collection = Self::get_collection(collection_id)?;970971            Self::token_exists(&target_collection, item_id)?;972973            // Transfer permissions check974            let bypasses_limits = target_collection.limits.owner_can_transfer &&975                Self::is_owner_or_admin_permissions(976                    &target_collection,977                    sender.clone(),978                );979980            let allowance_limit = if bypasses_limits {981                None982            } else if let Some(amount) = Self::owned_amount(983                sender.clone(),984                &target_collection,985                item_id,986            ) {987                Some(amount)988            } else {989                fail!(Error::<T>::NoPermission);990            };991992            if target_collection.access == AccessMode::WhiteList {993                Self::check_white_list(&target_collection, &sender)?;994                Self::check_white_list(&target_collection, &spender)?;995            }996997            let allowance: u128 = amount998                .checked_add(<Allowances<T>>::get(collection_id, (item_id, &sender, &spender)))999                .ok_or(Error::<T>::NumOverflow)?;1000            if let Some(limit) = allowance_limit {1001                ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1002            }1003            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);10041005            Self::deposit_event(RawEvent::Approved(target_collection.id, item_id, sender, spender, allowance));1006            Ok(())1007        }1008        1009        /// 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.1010        /// 1011        /// # Permissions1012        /// * Collection Owner1013        /// * Collection Admin1014        /// * Current NFT owner1015        /// * Address approved by current NFT owner1016        /// 1017        /// # Arguments1018        /// 1019        /// * from: Address that owns token.1020        /// 1021        /// * recipient: Address of token recipient.1022        /// 1023        /// * collection_id.1024        /// 1025        /// * item_id: ID of the item.1026        /// 1027        /// * value: Amount to transfer.1028        #[weight = <T as Config>::WeightInfo::transfer_from()]1029        #[transactional]1030        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {10311032            let sender = ensure_signed(origin)?;1033            let target_collection = Self::get_collection(collection_id)?;10341035            // Check approval1036            let approval: u128 = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));10371038            // Limits check1039            Self::is_correct_transfer(&target_collection, &recipient)?;10401041            // Transfer permissions check         1042            ensure!(1043                approval >= value || 1044                (1045                    target_collection.limits.owner_can_transfer &&1046                    Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1047                ),1048                Error::<T>::NoPermission1049            );10501051            if target_collection.access == AccessMode::WhiteList {1052                Self::check_white_list(&target_collection, &sender)?;1053                Self::check_white_list(&target_collection, &recipient)?;1054            }10551056            // Reduce approval by transferred amount or remove if remaining approval drops to 01057            if approval.saturating_sub(value) > 0 {1058                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1059            }1060            else {1061                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1062            }10631064            match target_collection.mode1065            {1066                CollectionMode::NFT => Self::transfer_nft(&target_collection, item_id, from.clone(), recipient.clone())?,1067                CollectionMode::Fungible(_)  => Self::transfer_fungible(&target_collection, value, &from, &recipient)?,1068                CollectionMode::ReFungible  => Self::transfer_refungible(&target_collection, item_id, value, from.clone(), recipient.clone())?,1069                _ => ()1070            };10711072            Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, from, recipient, value));1073            Ok(())1074        }10751076        // #[weight = 0]1077        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {10781079        //     // let no_perm_mes = "You do not have permissions to modify this collection";1080        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1081        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1082        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);10831084        //     // // on_nft_received  call10851086        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;10871088        //     Ok(())1089        // }10901091        /// Set off-chain data schema.1092        /// 1093        /// # Permissions1094        /// 1095        /// * Collection Owner1096        /// * Collection Admin1097        /// 1098        /// # Arguments1099        /// 1100        /// * collection_id.1101        /// 1102        /// * schema: String representing the offchain data schema.1103        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1104        #[transactional]1105        pub fn set_variable_meta_data (1106            origin,1107            collection_id: CollectionId,1108            item_id: TokenId,1109            data: Vec<u8>1110        ) -> DispatchResult {1111            let sender = ensure_signed(origin)?;1112            1113            let target_collection = Self::get_collection(collection_id)?;1114            Self::token_exists(&target_collection, item_id)?;11151116            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);11171118            // Modify permissions check1119            ensure!(Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1120                Self::is_owner_or_admin_permissions(&target_collection, sender.clone()),1121                Error::<T>::NoPermission);11221123            match target_collection.mode1124            {1125                CollectionMode::NFT => Self::set_nft_variable_data(&target_collection, item_id, data)?,1126                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(&target_collection, item_id, data)?,1127                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1128                _ => fail!(Error::<T>::UnexpectedCollectionType)1129            };11301131            Ok(())1132        }1133 1134        /// Set schema standard1135        /// ImageURL1136        /// Unique1137        /// 1138        /// # Permissions1139        /// 1140        /// * Collection Owner1141        /// * Collection Admin1142        /// 1143        /// # Arguments1144        /// 1145        /// * collection_id.1146        /// 1147        /// * schema: SchemaVersion: enum1148        #[weight = <T as Config>::WeightInfo::set_schema_version()]1149        #[transactional]1150        pub fn set_schema_version(1151            origin,1152            collection_id: CollectionId,1153            version: SchemaVersion1154        ) -> DispatchResult {1155            let sender = ensure_signed(origin)?;1156            let mut target_collection = Self::get_collection(collection_id)?;1157            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;1158            target_collection.schema_version = version;1159            Self::save_collection(target_collection);11601161            Ok(())1162        }11631164        /// Set off-chain data schema.1165        /// 1166        /// # Permissions1167        /// 1168        /// * Collection Owner1169        /// * Collection Admin1170        /// 1171        /// # Arguments1172        /// 1173        /// * collection_id.1174        /// 1175        /// * schema: String representing the offchain data schema.1176        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1177        #[transactional]1178        pub fn set_offchain_schema(1179            origin,1180            collection_id: CollectionId,1181            schema: Vec<u8>1182        ) -> DispatchResult {1183            let sender = ensure_signed(origin)?;1184            let mut target_collection = Self::get_collection(collection_id)?;1185            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;11861187            // check schema limit1188            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");11891190            target_collection.offchain_schema = schema;1191            Self::save_collection(target_collection);11921193            Ok(())1194        }11951196        /// Set const on-chain data schema.1197        /// 1198        /// # Permissions1199        /// 1200        /// * Collection Owner1201        /// * Collection Admin1202        /// 1203        /// # Arguments1204        /// 1205        /// * collection_id.1206        /// 1207        /// * schema: String representing the const on-chain data schema.1208        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1209        #[transactional]1210        pub fn set_const_on_chain_schema (1211            origin,1212            collection_id: CollectionId,1213            schema: Vec<u8>1214        ) -> DispatchResult {1215            let sender = ensure_signed(origin)?;1216            let mut target_collection = Self::get_collection(collection_id)?;1217            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;12181219            // check schema limit1220            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");12211222            target_collection.const_on_chain_schema = schema;1223            Self::save_collection(target_collection);12241225            Ok(())1226        }12271228        /// Set variable on-chain data schema.1229        /// 1230        /// # Permissions1231        /// 1232        /// * Collection Owner1233        /// * Collection Admin1234        /// 1235        /// # Arguments1236        /// 1237        /// * collection_id.1238        /// 1239        /// * schema: String representing the variable on-chain data schema.1240        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1241        #[transactional]1242        pub fn set_variable_on_chain_schema (1243            origin,1244            collection_id: CollectionId,1245            schema: Vec<u8>1246        ) -> DispatchResult {1247            let sender = ensure_signed(origin)?;1248            let mut target_collection = Self::get_collection(collection_id)?;1249            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;12501251            // check schema limit1252            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");12531254            target_collection.variable_on_chain_schema = schema;1255            Self::save_collection(target_collection);12561257            Ok(())1258        }12591260        // Sudo permissions function1261        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1262        #[transactional]1263        pub fn set_chain_limits(1264            origin,1265            limits: ChainLimits1266        ) -> DispatchResult {12671268            #[cfg(not(feature = "runtime-benchmarks"))]1269            ensure_root(origin)?;12701271            <ChainLimit>::put(limits);1272            Ok(())1273        }12741275        /// Enable smart contract self-sponsoring.1276        /// 1277        /// # Permissions1278        /// 1279        /// * Contract Owner1280        /// 1281        /// # Arguments1282        /// 1283        /// * contract address1284        /// * enable flag1285        /// 1286        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1287        #[transactional]1288        pub fn enable_contract_sponsoring(1289            origin,1290            contract_address: T::AccountId,1291            enable: bool1292        ) -> DispatchResult {12931294            let sender = ensure_signed(origin)?;12951296            #[cfg(feature = "runtime-benchmarks")]1297            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());12981299            Self::ensure_contract_owned(sender, &contract_address)?;13001301            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1302            Ok(())1303        }13041305        /// Set the rate limit for contract sponsoring to specified number of blocks.1306        /// 1307        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1308        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1309        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1310        /// from contract endowment if there are at least B blocks between such transactions. 1311        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1312        /// 1313        /// # Permissions1314        /// 1315        /// * Contract Owner1316        /// 1317        /// # Arguments1318        /// 1319        /// -`contract_address`: Address of the contract to sponsor1320        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1321        /// 1322        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1323        #[transactional]1324        pub fn set_contract_sponsoring_rate_limit(1325            origin,1326            contract_address: T::AccountId,1327            rate_limit: T::BlockNumber1328        ) -> DispatchResult {1329            let sender = ensure_signed(origin)?;13301331            #[cfg(feature = "runtime-benchmarks")]1332            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13331334            Self::ensure_contract_owned(sender, &contract_address)?;1335            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1336            Ok(())1337        }13381339        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1340        /// 1341        /// # Permissions1342        /// 1343        /// * Address that deployed smart contract.1344        /// 1345        /// # Arguments1346        /// 1347        /// -`contract_address`: Address of the contract.1348        /// 1349        /// - `enable`: .  1350        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1351        #[transactional]1352        pub fn toggle_contract_white_list(1353            origin,1354            contract_address: T::AccountId,1355            enable: bool1356        ) -> DispatchResult {1357            let sender = ensure_signed(origin)?;13581359            #[cfg(feature = "runtime-benchmarks")]1360            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13611362            Self::ensure_contract_owned(sender, &contract_address)?;1363            if enable {1364                <ContractWhiteListEnabled<T>>::insert(contract_address, true);1365            } else {1366                <ContractWhiteListEnabled<T>>::remove(contract_address);1367            }1368            Ok(())1369        }1370        1371        /// Add an address to smart contract white list.1372        /// 1373        /// # Permissions1374        /// 1375        /// * Address that deployed smart contract.1376        /// 1377        /// # Arguments1378        /// 1379        /// -`contract_address`: Address of the contract.1380        ///1381        /// -`account_address`: Address to add.1382        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1383        #[transactional]1384        pub fn add_to_contract_white_list(1385            origin,1386            contract_address: T::AccountId,1387            account_address: T::AccountId1388        ) -> DispatchResult {1389            let sender = ensure_signed(origin)?;13901391            #[cfg(feature = "runtime-benchmarks")]1392            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1393            1394            Self::ensure_contract_owned(sender, &contract_address)?;      1395            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1396            Ok(())1397        }13981399        /// Remove an address from smart contract white list.1400        /// 1401        /// # Permissions1402        /// 1403        /// * Address that deployed smart contract.1404        /// 1405        /// # Arguments1406        /// 1407        /// -`contract_address`: Address of the contract.1408        ///1409        /// -`account_address`: Address to remove.1410        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1411        #[transactional]1412        pub fn remove_from_contract_white_list(1413            origin,1414            contract_address: T::AccountId,1415            account_address: T::AccountId1416        ) -> DispatchResult {1417            let sender = ensure_signed(origin)?;14181419            #[cfg(feature = "runtime-benchmarks")]1420            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14211422            Self::ensure_contract_owned(sender, &contract_address)?;1423            <ContractWhiteList<T>>::remove(contract_address, account_address);1424            Ok(())1425        }14261427        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1428        #[transactional]1429        pub fn set_collection_limits(1430            origin,1431            collection_id: u32,1432            new_limits: CollectionLimits<T::BlockNumber>,1433        ) -> DispatchResult {1434            let sender = ensure_signed(origin)?;1435            let mut target_collection = Self::get_collection(collection_id)?;1436            Self::check_owner_permissions(&target_collection, sender.clone())?;1437            let old_limits = &target_collection.limits;1438            let chain_limits = ChainLimit::get();14391440            // collection bounds1441            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1442                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1443                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1444                Error::<T>::CollectionLimitBoundsExceeded);14451446            // token_limit   check  prev1447            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1448            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);14491450            ensure!(1451                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1452                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1453                Error::<T>::OwnerPermissionsCantBeReverted,1454            );14551456            target_collection.limits = new_limits;1457            Self::save_collection(target_collection);14581459            Ok(())1460        } 1461    }1462}14631464impl<T: Config> Module<T> {1465    pub fn create_item_internal(sender: T::AccountId, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1466        let target_collection = Self::get_collection(collection_id)?;14671468        Self::can_create_items_in_collection(&target_collection, &sender, &owner, 1)?;1469        Self::validate_create_item_args(&target_collection, &data)?;1470        Self::create_item_no_validation(&target_collection, owner, data)?;14711472        Ok(())1473    }14741475    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1476        // Limits check1477        Self::is_correct_transfer(target_collection, &recipient)?;14781479        // Transfer permissions check1480        ensure!(Self::is_item_owner(sender.clone(), target_collection, item_id) ||1481            Self::is_owner_or_admin_permissions(target_collection, sender.clone()),1482            Error::<T>::NoPermission);14831484        if target_collection.access == AccessMode::WhiteList {1485            Self::check_white_list(target_collection, &sender)?;1486            Self::check_white_list(target_collection, &recipient)?;1487        }14881489        match target_collection.mode1490        {1491            CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1492            CollectionMode::Fungible(_)  => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1493            CollectionMode::ReFungible  => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1494            _ => ()1495        };14961497        Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender, recipient, value));14981499        Ok(())1500    }150115021503    fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::AccountId) -> DispatchResult {1504        let collection_id = collection.id;15051506        // check token limit and account token limit1507        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1508        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1509        1510        Ok(())1511    }15121513    fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::AccountId, owner: &T::AccountId, amount: u32) -> DispatchResult {1514        let collection_id = collection.id;15151516        // check token limit and account token limit1517        let total_items: u32 = ItemListIndex::get(collection_id)1518            .checked_add(amount)1519            .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1520        let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner).len() as u32)1521            .checked_add(amount)1522            .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1523        ensure!(collection.limits.token_limit >= total_items,  Error::<T>::CollectionTokenLimitExceeded);1524        ensure!(collection.limits.account_token_ownership_limit >= account_items,  Error::<T>::AccountTokenLimitExceeded);15251526        if !Self::is_owner_or_admin_permissions(collection, sender.clone()) {1527            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1528            Self::check_white_list(collection, owner)?;1529            Self::check_white_list(collection, sender)?;1530        }15311532        Ok(())1533    }15341535    fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1536        match target_collection.mode1537        {1538            CollectionMode::NFT => {1539                if let CreateItemData::NFT(data) = data {1540                    // check sizes1541                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1542                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1543                } else {1544                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1545                }1546            },1547            CollectionMode::Fungible(_) => {1548                if let CreateItemData::Fungible(_) = data {1549                } else {1550                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1551                }1552            },1553            CollectionMode::ReFungible => {1554                if let CreateItemData::ReFungible(data) = data {15551556                    // check sizes1557                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1558                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);15591560                    // Check refungibility limits1561                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1562                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1563                } else {1564                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1565                }1566            },1567            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1568        };15691570        Ok(())1571    }15721573    fn create_item_no_validation(collection: &CollectionHandle<T>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1574        match data1575        {1576            CreateItemData::NFT(data) => {1577                let item = NftItemType {1578                    owner: owner.clone(),1579                    const_data: data.const_data,1580                    variable_data: data.variable_data1581                };15821583                Self::add_nft_item(collection, item)?;1584            },1585            CreateItemData::Fungible(data) => {1586                Self::add_fungible_item(collection, &owner, data.value)?;1587            },1588            CreateItemData::ReFungible(data) => {1589                let mut owner_list = Vec::new();1590                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});15911592                let item = ReFungibleItemType {1593                    owner: owner_list,1594                    const_data: data.const_data,1595                    variable_data: data.variable_data1596                };15971598                Self::add_refungible_item(collection, item)?;1599            }1600        };16011602        Ok(())1603    }16041605    fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::AccountId, value: u128) -> DispatchResult {1606        let collection_id = collection.id;16071608        // Does new owner already have an account?1609        let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner).value;16101611        // Mint 1612        let item = FungibleItemType {1613            value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1614        };1615        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16161617        // Update balance1618        let new_balance = <Balance<T>>::get(collection_id, owner)1619            .checked_add(value)1620            .ok_or(Error::<T>::NumOverflow)?;1621        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);16221623        Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1624        Ok(())1625    }16261627    fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1628        let collection_id = collection.id;16291630        let current_index = <ItemListIndex>::get(collection_id)1631            .checked_add(1)1632            .ok_or(Error::<T>::NumOverflow)?;1633        let itemcopy = item.clone();16341635        ensure!(1636            item.owner.len() == 1,1637            Error::<T>::BadCreateRefungibleCall,1638        );1639        let item_owner = item.owner.first().expect("only one owner is defined");16401641        let value = item_owner.fraction;1642        let owner = item_owner.owner.clone();16431644        Self::add_token_index(collection_id, current_index, &owner)?;16451646        <ItemListIndex>::insert(collection_id, current_index);1647        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);16481649        // Update balance1650        let new_balance = <Balance<T>>::get(collection_id, &owner)1651            .checked_add(value)1652            .ok_or(Error::<T>::NumOverflow)?;1653        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);16541655        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1656        Ok(())1657    }16581659    fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::AccountId>) -> DispatchResult {1660        let collection_id = collection.id;16611662        let current_index = <ItemListIndex>::get(collection_id)1663            .checked_add(1)1664            .ok_or(Error::<T>::NumOverflow)?;16651666        let item_owner = item.owner.clone();1667        Self::add_token_index(collection_id, current_index, &item.owner)?;16681669        <ItemListIndex>::insert(collection_id, current_index);1670        <NftItemList<T>>::insert(collection_id, current_index, item);16711672        // Update balance1673        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1674            .checked_add(1)1675            .ok_or(Error::<T>::NumOverflow)?;1676        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);16771678        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));1679        Ok(())1680    }16811682    fn burn_refungible_item(1683        collection: &CollectionHandle<T>,1684        item_id: TokenId,1685        owner: &T::AccountId,1686    ) -> DispatchResult {1687        let collection_id = collection.id;16881689        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1690            .ok_or(Error::<T>::TokenNotFound)?;1691        let rft_balance = token1692            .owner1693            .iter()1694            .find(|&i| i.owner == *owner)1695            .ok_or(Error::<T>::TokenNotFound)?;1696        Self::remove_token_index(collection_id, item_id, owner)?;16971698        // update balance1699        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1700            .checked_sub(rft_balance.fraction)1701            .ok_or(Error::<T>::NumOverflow)?;1702        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);17031704        // Re-create owners list with sender removed1705        let index = token1706            .owner1707            .iter()1708            .position(|i| i.owner == *owner)1709            .expect("owned item is exists");1710        token.owner.remove(index);1711        let owner_count = token.owner.len();17121713        // Burn the token completely if this was the last (only) owner1714        if owner_count == 0 {1715            <ReFungibleItemList<T>>::remove(collection_id, item_id);1716            <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1717        }1718        else {1719            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1720        }17211722        Ok(())1723    }17241725    fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1726        let collection_id = collection.id;17271728        let item = <NftItemList<T>>::get(collection_id, item_id)1729            .ok_or(Error::<T>::TokenNotFound)?;1730        Self::remove_token_index(collection_id, item_id, &item.owner)?;17311732        // update balance1733        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1734            .checked_sub(1)1735            .ok_or(Error::<T>::NumOverflow)?;1736        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1737        <NftItemList<T>>::remove(collection_id, item_id);1738        <VariableMetaDataBasket<T>>::remove(collection_id, item_id);17391740        Ok(())1741    }17421743    fn burn_fungible_item(owner: &T::AccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {1744        let collection_id = collection.id;17451746        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1747        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);17481749        // update balance1750        let new_balance = <Balance<T>>::get(collection_id, owner)1751            .checked_sub(value)1752            .ok_or(Error::<T>::NumOverflow)?;1753        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17541755        if balance.value - value > 0 {1756            balance.value -= value;1757            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1758        }1759        else {1760            <FungibleItemList<T>>::remove(collection_id, owner);1761        }17621763        Ok(())1764    }17651766    pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1767        Ok(<CollectionById<T>>::get(collection_id)1768            .map(|collection| CollectionHandle {1769                id: collection_id,1770                collection1771            })1772            .ok_or(Error::<T>::CollectionNotFound)?)1773    }17741775    fn save_collection(collection: CollectionHandle<T>) {1776        <CollectionById<T>>::insert(collection.id, collection.collection);1777    }17781779    fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: T::AccountId) -> DispatchResult {1780        ensure!(1781            subject == target_collection.owner,1782            Error::<T>::NoPermission1783        );17841785        Ok(())1786    }17871788    fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: T::AccountId) -> bool {1789        subject == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)1790    }17911792    fn check_owner_or_admin_permissions(1793        collection: &CollectionHandle<T>,1794        subject: T::AccountId,1795    ) -> DispatchResult {1796        ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);17971798        Ok(())1799    }18001801    fn owned_amount(1802        subject: T::AccountId,1803        target_collection: &CollectionHandle<T>,1804        item_id: TokenId,1805    ) -> Option<u128> {1806        let collection_id = target_collection.id;18071808        match target_collection.mode {1809            CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == subject)1810                .then(|| 1),1811            CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject)1812                .value),1813            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1814                .owner1815                .iter()1816                .find(|i| i.owner == subject)1817                .map(|i| i.fraction),1818            CollectionMode::Invalid => None,1819        }1820    }18211822    fn is_item_owner(subject: T::AccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {1823        match target_collection.mode {1824            CollectionMode::Fungible(_) => true,1825            _ => Self::owned_amount(subject, target_collection, item_id).is_some(),1826        }1827    }18281829    fn check_white_list(collection: &CollectionHandle<T>, address: &T::AccountId) -> DispatchResult {1830        let collection_id = collection.id;18311832        let mes = Error::<T>::AddresNotInWhiteList;1833        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);18341835        Ok(())1836    }18371838    /// Check if token exists. In case of Fungible, check if there is an entry for 1839    /// the owner in fungible balances double map1840    fn token_exists(1841        target_collection: &CollectionHandle<T>,1842        item_id: TokenId,1843    ) -> DispatchResult {1844        let collection_id = target_collection.id;1845        let exists = match target_collection.mode1846        {1847            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1848            CollectionMode::Fungible(_)  => true,1849            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1850            _ => false1851        };18521853        ensure!(exists == true, Error::<T>::TokenNotFound);1854        Ok(())1855    }18561857    fn transfer_fungible(1858        collection: &CollectionHandle<T>,1859        value: u128,1860        owner: &T::AccountId,1861        recipient: &T::AccountId,1862    ) -> DispatchResult {1863        let collection_id = collection.id;18641865        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1866        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);18671868        // Send balance to recipient (updates balanceOf of recipient)1869        Self::add_fungible_item(collection, recipient, value)?;18701871        // update balanceOf of sender1872        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);18731874        // Reduce or remove sender1875        if balance.value == value {1876            <FungibleItemList<T>>::remove(collection_id, owner);1877        }1878        else {1879            balance.value -= value;1880            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1881        }18821883        Ok(())1884    }18851886    fn transfer_refungible(1887        collection: &CollectionHandle<T>,1888        item_id: TokenId,1889        value: u128,1890        owner: T::AccountId,1891        new_owner: T::AccountId,1892    ) -> DispatchResult {1893        let collection_id = collection.id;1894        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)1895            .ok_or(Error::<T>::TokenNotFound)?;18961897        let item = full_item1898            .owner1899            .iter()1900            .filter(|i| i.owner == owner)1901            .next()1902            .ok_or(Error::<T>::TokenNotFound)?;1903        let amount = item.fraction;19041905        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19061907        // update balance1908        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1909            .checked_sub(value)1910            .ok_or(Error::<T>::NumOverflow)?;1911        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19121913        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1914            .checked_add(value)1915            .ok_or(Error::<T>::NumOverflow)?;1916        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19171918        let old_owner = item.owner.clone();1919        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19201921        // transfer1922        if amount == value && !new_owner_has_account {1923            // change owner1924            // new owner do not have account1925            let mut new_full_item = full_item.clone();1926            new_full_item1927                .owner1928                .iter_mut()1929                .find(|i| i.owner == owner)1930                .expect("old owner does present in refungible")1931                .owner = new_owner.clone();1932            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19331934            // update index collection1935            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;1936        } else {1937            let mut new_full_item = full_item.clone();1938            new_full_item1939                .owner1940                .iter_mut()1941                .find(|i| i.owner == owner)1942                .expect("old owner does present in refungible")1943                .fraction -= value;19441945            // separate amount1946            if new_owner_has_account {1947                // new owner has account1948                new_full_item1949                    .owner1950                    .iter_mut()1951                    .find(|i| i.owner == new_owner)1952                    .expect("new owner has account")1953                    .fraction += value;1954            } else {1955                // new owner do not have account1956                new_full_item.owner.push(Ownership {1957                    owner: new_owner.clone(),1958                    fraction: value,1959                });1960                Self::add_token_index(collection_id, item_id, &new_owner)?;1961            }19621963            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1964        }19651966        Ok(())1967    }19681969    fn transfer_nft(1970        collection: &CollectionHandle<T>,1971        item_id: TokenId,1972        sender: T::AccountId,1973        new_owner: T::AccountId,1974    ) -> DispatchResult {1975        let collection_id = collection.id;1976        let mut item = <NftItemList<T>>::get(collection_id, item_id)1977            .ok_or(Error::<T>::TokenNotFound)?;19781979        ensure!(1980            sender == item.owner,1981            Error::<T>::MustBeTokenOwner1982        );19831984        // update balance1985        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1986            .checked_sub(1)1987            .ok_or(Error::<T>::NumOverflow)?;1988        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19891990        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1991            .checked_add(1)1992            .ok_or(Error::<T>::NumOverflow)?;1993        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19941995        // change owner1996        let old_owner = item.owner.clone();1997        item.owner = new_owner.clone();1998        <NftItemList<T>>::insert(collection_id, item_id, item);19992000        // update index collection2001        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;20022003        Ok(())2004    }2005    2006    fn set_re_fungible_variable_data(2007        collection: &CollectionHandle<T>,2008        item_id: TokenId,2009        data: Vec<u8>2010    ) -> DispatchResult {2011        let collection_id = collection.id;2012        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2013            .ok_or(Error::<T>::TokenNotFound)?;20142015        item.variable_data = data;20162017        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20182019        Ok(())2020    }20212022    fn set_nft_variable_data(2023        collection: &CollectionHandle<T>,2024        item_id: TokenId,2025        data: Vec<u8>2026    ) -> DispatchResult {2027        let collection_id = collection.id;2028        let mut item = <NftItemList<T>>::get(collection_id, item_id)2029            .ok_or(Error::<T>::TokenNotFound)?;2030        2031        item.variable_data = data;20322033        <NftItemList<T>>::insert(collection_id, item_id, item);2034        2035        Ok(())2036    }20372038    fn init_collection(item: &Collection<T>) {2039        // check params2040        assert!(2041            item.decimal_points <= MAX_DECIMAL_POINTS,2042            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2043        );2044        assert!(2045            item.name.len() <= 64,2046            "Collection name can not be longer than 63 char"2047        );2048        assert!(2049            item.name.len() <= 256,2050            "Collection description can not be longer than 255 char"2051        );2052        assert!(2053            item.token_prefix.len() <= 16,2054            "Token prefix can not be longer than 15 char"2055        );20562057        // Generate next collection ID2058        let next_id = CreatedCollectionCount::get()2059            .checked_add(1)2060            .unwrap();20612062        CreatedCollectionCount::put(next_id);2063    }20642065    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2066        let current_index = <ItemListIndex>::get(collection_id)2067            .checked_add(1)2068            .unwrap();20692070        let item_owner = item.owner.clone();2071        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();20722073        <ItemListIndex>::insert(collection_id, current_index);20742075        // Update balance2076        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2077            .checked_add(1)2078            .unwrap();2079        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2080    }20812082    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2083        let current_index = <ItemListIndex>::get(collection_id)2084            .checked_add(1)2085            .unwrap();20862087        Self::add_token_index(collection_id, current_index, owner).unwrap();20882089        <ItemListIndex>::insert(collection_id, current_index);20902091        // Update balance2092        let new_balance = <Balance<T>>::get(collection_id, owner)2093            .checked_add(item.value)2094            .unwrap();2095        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2096    }20972098    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2099        let current_index = <ItemListIndex>::get(collection_id)2100            .checked_add(1)2101            .unwrap();21022103        let value = item.owner.first().unwrap().fraction;2104        let owner = item.owner.first().unwrap().owner.clone();21052106        Self::add_token_index(collection_id, current_index, &owner).unwrap();21072108        <ItemListIndex>::insert(collection_id, current_index);21092110        // Update balance2111        let new_balance = <Balance<T>>::get(collection_id, &owner)2112            .checked_add(value)2113            .unwrap();2114        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2115    }21162117    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {2118        // add to account limit2119        if <AccountItemCount<T>>::contains_key(owner) {21202121            // bound Owned tokens by a single address2122            let count = <AccountItemCount<T>>::get(owner);2123            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21242125            <AccountItemCount<T>>::insert(owner.clone(), count2126                .checked_add(1)2127                .ok_or(Error::<T>::NumOverflow)?);2128        }2129        else {2130            <AccountItemCount<T>>::insert(owner.clone(), 1);2131        }21322133        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2134        if list_exists {2135            let mut list = <AddressTokens<T>>::get(collection_id, owner);2136            let item_contains = list.contains(&item_index.clone());21372138            if !item_contains {2139                list.push(item_index.clone());2140            }21412142            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2143        } else {2144            let mut itm = Vec::new();2145            itm.push(item_index.clone());2146            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2147        }21482149        Ok(())2150    }21512152    fn remove_token_index(2153        collection_id: CollectionId,2154        item_index: TokenId,2155        owner: &T::AccountId,2156    ) -> DispatchResult {21572158        // update counter2159        <AccountItemCount<T>>::insert(owner.clone(), 2160            <AccountItemCount<T>>::get(owner)2161            .checked_sub(1)2162            .ok_or(Error::<T>::NumOverflow)?);216321642165        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2166        if list_exists {2167            let mut list = <AddressTokens<T>>::get(collection_id, owner);2168            let item_contains = list.contains(&item_index.clone());21692170            if item_contains {2171                list.retain(|&item| item != item_index);2172                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2173            }2174        }21752176        Ok(())2177    }21782179    fn move_token_index(2180        collection_id: CollectionId,2181        item_index: TokenId,2182        old_owner: &T::AccountId,2183        new_owner: &T::AccountId,2184    ) -> DispatchResult {2185        Self::remove_token_index(collection_id, item_index, old_owner)?;2186        Self::add_token_index(collection_id, item_index, new_owner)?;21872188        Ok(())2189    }2190    2191    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2192        ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);21932194        Ok(())2195    }2196}