git.delta.rocks / unique-network / refs/commits / 353c1c657ac7

difftreelog

refactor! rewrite collections accessors

Yaroslav Bolyukin2021-03-05parent: #a84a730.patch.diff
in: master
Squashed from multiple

4 files changed

modifiednode/src/chain_spec.rsdiffbeforeafterboth
--- a/node/src/chain_spec.rs
+++ b/node/src/chain_spec.rs
@@ -179,9 +179,10 @@
                 .collect(),
         }),
         pallet_nft: Some(NftConfig {
-            collection: vec![(
+            collection_id: vec![(
                 1,
-                CollectionType {
+                Collection {
+					id: 1,
                     owner: get_account_id_from_seed::<sr25519::Public>("Alice"),
                     mode: CollectionMode::NFT,
                     access: AccessMode::Normal,
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
before · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, IsSubType,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial, DispatchClass,29    },30    StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use sp_runtime::traits::StaticLookup;45use pallet_contracts::chain_extension::UncheckedFrom;46use transaction_payment::OnChargeTransaction;4748#[cfg(test)]49mod mock;5051#[cfg(test)]52mod tests;5354mod default_weights;5556pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;57pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;58pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;59pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6061// Structs62// #region6364pub type CollectionId = u32;65pub type TokenId = u32;66pub type DecimalPoints = u8;6768#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]69#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]70pub enum CollectionMode {71    Invalid,72    NFT,73    // decimal points74    Fungible(DecimalPoints),75    ReFungible,76}7778impl Default for CollectionMode {79    fn default() -> Self {80        Self::Invalid81    }82}8384impl Into<u8> for CollectionMode {85    fn into(self) -> u8 {86        match self {87            CollectionMode::Invalid => 0,88            CollectionMode::NFT => 1,89            CollectionMode::Fungible(_) => 2,90            CollectionMode::ReFungible => 3,91        }92    }93}9495#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]96#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]97pub enum AccessMode {98    Normal,99    WhiteList,100}101impl Default for AccessMode {102    fn default() -> Self {103        Self::Normal104    }105}106107#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]108#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]109pub enum SchemaVersion {110    ImageURL,111    Unique,112}113impl Default for SchemaVersion {114    fn default() -> Self {115        Self::ImageURL116    }117}118119#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]120#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]121pub struct Ownership<AccountId> {122    pub owner: AccountId,123    pub fraction: u128,124}125126#[derive(Encode, Decode, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub struct CollectionType<T: Config> {129    pub owner: T::AccountId,130    pub mode: CollectionMode,131    pub access: AccessMode,132    pub decimal_points: DecimalPoints,133    pub name: Vec<u16>,        // 64 include null escape char134    pub description: Vec<u16>, // 256 include null escape char135    pub token_prefix: Vec<u8>, // 16 include null escape char136    pub mint_mode: bool,137    pub offchain_schema: Vec<u8>,138    pub schema_version: SchemaVersion,139    pub sponsor: T::AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender140    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.141    pub limits: CollectionLimits<T::BlockNumber>, // Collection private restrictions 142    pub variable_on_chain_schema: Vec<u8>, //143    pub const_on_chain_schema: Vec<u8>, //144}145146#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]147#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]148pub struct NftItemType<AccountId> {149    pub owner: AccountId,150    pub const_data: Vec<u8>,151    pub variable_data: Vec<u8>,152}153154#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]155#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]156pub struct FungibleItemType {157    pub value: u128,158}159160#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]161#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]162pub struct ReFungibleItemType<AccountId> {163    pub owner: Vec<Ownership<AccountId>>,164    pub const_data: Vec<u8>,165    pub variable_data: Vec<u8>,166}167168// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]169// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170// pub struct VestingItem<AccountId, Moment> {171//     pub sender: AccountId,172//     pub recipient: AccountId,173//     pub collection_id: CollectionId,174//     pub item_id: TokenId,175//     pub amount: u64,176//     pub vesting_date: Moment,177// }178179#[derive(Encode, Decode, Debug, Clone, PartialEq)]180#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]181pub struct CollectionLimits<BlockNumber: Encode + Decode> {182    pub account_token_ownership_limit: u32,183    pub sponsored_data_size: u32,184    /// None - setVariableMetadata is not sponsored185    /// Some(v) - setVariableMetadata is sponsored 186    ///           if there is v block between txs187    pub sponsored_data_rate_limit: Option<BlockNumber>,188    pub token_limit: u32,189190    // Timeouts for item types in passed blocks191    pub sponsor_transfer_timeout: u32,192    pub owner_can_transfer: bool,193    pub owner_can_destroy: bool,194}195196impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {197    fn default() -> Self {198        Self { 199            account_token_ownership_limit: 10_000_000, 200            token_limit: u32::max_value(),201            sponsored_data_size: u32::MAX, 202            sponsored_data_rate_limit: None,203            sponsor_transfer_timeout: 14400,204            owner_can_transfer: true,205            owner_can_destroy: true206        }207    }208}209210#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]211#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]212pub struct ChainLimits {213    pub collection_numbers_limit: u32,214    pub account_token_ownership_limit: u32,215    pub collections_admins_limit: u64,216    pub custom_data_limit: u32,217218    // Timeouts for item types in passed blocks219    pub nft_sponsor_transfer_timeout: u32,220    pub fungible_sponsor_transfer_timeout: u32,221    pub refungible_sponsor_transfer_timeout: u32,222223    // Schema limits224    pub offchain_schema_limit: u32,225    pub variable_on_chain_schema_limit: u32,226    pub const_on_chain_schema_limit: u32,227}228229pub trait WeightInfo {230	fn create_collection() -> Weight;231	fn destroy_collection() -> Weight;232	fn add_to_white_list() -> Weight;233	fn remove_from_white_list() -> Weight;234    fn set_public_access_mode() -> Weight;235    fn set_mint_permission() -> Weight;236    fn change_collection_owner() -> Weight;237    fn add_collection_admin() -> Weight;238    fn remove_collection_admin() -> Weight;239    fn set_collection_sponsor() -> Weight;240    fn confirm_sponsorship() -> Weight;241    fn remove_collection_sponsor() -> Weight;242    fn create_item(s: usize) -> Weight;243    fn burn_item() -> Weight;244    fn transfer() -> Weight;245    fn approve() -> Weight;246    fn transfer_from() -> Weight;247    fn set_offchain_schema() -> Weight;248    fn set_const_on_chain_schema() -> Weight;249    fn set_variable_on_chain_schema() -> Weight;250    fn set_variable_meta_data() -> Weight;251    fn enable_contract_sponsoring() -> Weight;252    fn set_schema_version() -> Weight;253    fn set_chain_limits() -> Weight;254    fn set_contract_sponsoring_rate_limit() -> Weight;255    fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;256    fn toggle_contract_white_list() -> Weight;257    fn add_to_contract_white_list() -> Weight;258    fn remove_from_contract_white_list() -> Weight;259    fn set_collection_limits() -> Weight;260}261262#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]263#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]264pub struct CreateNftData {265    pub const_data: Vec<u8>,266    pub variable_data: Vec<u8>,267}268269#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]270#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]271pub struct CreateFungibleData {272    pub value: u128,273}274275#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]276#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]277pub struct CreateReFungibleData {278    pub const_data: Vec<u8>,279    pub variable_data: Vec<u8>,280    pub pieces: u128,281}282283#[derive(Encode, Decode, Debug, Clone, PartialEq)]284#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]285pub enum CreateItemData {286    NFT(CreateNftData),287    Fungible(CreateFungibleData),288    ReFungible(CreateReFungibleData),289}290291impl CreateItemData {292    pub fn len(&self) -> usize {293        let len = match self {294            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),295            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),296            _ => 0297        };298        299        return len;300    }301}302303impl From<CreateNftData> for CreateItemData {304    fn from(item: CreateNftData) -> Self {305        CreateItemData::NFT(item)306    }307}308309impl From<CreateReFungibleData> for CreateItemData {310    fn from(item: CreateReFungibleData) -> Self {311        CreateItemData::ReFungible(item)312    }313}314315impl From<CreateFungibleData> for CreateItemData {316    fn from(item: CreateFungibleData) -> Self {317        CreateItemData::Fungible(item)318    }319}320321322decl_error! {323	/// Error for non-fungible-token module.324	pub enum Error for Module<T: Config> {325        /// Total collections bound exceeded.326        TotalCollectionsLimitExceeded,327		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.328        CollectionDecimalPointLimitExceeded, 329        /// Collection name can not be longer than 63 char.330        CollectionNameLimitExceeded, 331        /// Collection description can not be longer than 255 char.332        CollectionDescriptionLimitExceeded, 333        /// Token prefix can not be longer than 15 char.334        CollectionTokenPrefixLimitExceeded,335        /// This collection does not exist.336        CollectionNotFound,337        /// Item not exists.338        TokenNotFound,339        /// Admin not found340        AdminNotFound,341        /// Arithmetic calculation overflow.342        NumOverflow,       343        /// Account already has admin role.344        AlreadyAdmin,  345        /// You do not own this collection.346        NoPermission,347        /// This address is not set as sponsor, use setCollectionSponsor first.348        ConfirmUnsetSponsorFail,349        /// Collection is not in mint mode.350        PublicMintingNotAllowed,351        /// Sender parameter and item owner must be equal.352        MustBeTokenOwner,353        /// Item balance not enough.354        TokenValueTooLow,355        /// Size of item is too large.356        NftSizeLimitExceeded,357        /// No approve found358        ApproveNotFound,359        /// Requested value more than approved.360        TokenValueNotEnough,361        /// Only approved addresses can call this method.362        ApproveRequired,363        /// Address is not in white list.364        AddresNotInWhiteList,365        /// Number of collection admins bound exceeded.366        CollectionAdminsLimitExceeded,367        /// Owned tokens by a single address bound exceeded.368        AddressOwnershipLimitExceeded,369        /// Length of items properties must be greater than 0.370        EmptyArgument,371        /// const_data exceeded data limit.372        TokenConstDataLimitExceeded,373        /// variable_data exceeded data limit.374        TokenVariableDataLimitExceeded,375        /// Not NFT item data used to mint in NFT collection.376        NotNftDataUsedToMintNftCollectionToken,377        /// Not Fungible item data used to mint in Fungible collection.378        NotFungibleDataUsedToMintFungibleCollectionToken,379        /// Not Re Fungible item data used to mint in Re Fungible collection.380        NotReFungibleDataUsedToMintReFungibleCollectionToken,381        /// Unexpected collection type.382        UnexpectedCollectionType,383        /// Can't store metadata in fungible tokens.384        CantStoreMetadataInFungibleTokens,385        /// Collection token limit exceeded386        CollectionTokenLimitExceeded,387        /// Account token limit exceeded per collection388        AccountTokenLimitExceeded,389        /// Collection limit bounds per collection exceeded390        CollectionLimitBoundsExceeded,391        /// Tried to enable permissions which are only permitted to be disabled392        OwnerPermissionsCantBeReverted,393        /// Schema data size limit bound exceeded394        SchemaDataLimitExceeded,395        /// Maximum refungibility exceeded396        WrongRefungiblePieces397	}398}399400pub trait Config: system::Config + Sized + transaction_payment::Config + pallet_contracts::Config {401    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;402403    /// Weight information for extrinsics in this pallet.404	type WeightInfo: WeightInfo;405}406407#[cfg(feature = "runtime-benchmarks")]408mod benchmarking;409410// #endregion411412decl_storage! {413    trait Store for Module<T: Config> as Nft {414415        // Private members416        NextCollectionID: CollectionId;417        CreatedCollectionCount: u32;418        ChainVersion: u64;419        ItemListIndex: map hasher(identity) CollectionId => TokenId;420421        // Chain limits struct422        pub ChainLimit get(fn chain_limit) config(): ChainLimits;423424        // Bound counters425        CollectionCount: u32;426        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;427428        // Basic collections429        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => Option<CollectionType<T>> = None;430        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;431        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;432433        /// Balance owner per collection map434        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;435436        /// second parameter: item id + owner account id + spender account id437        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;438439        /// Item collections440        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;441        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;442        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;443444        /// Index list445        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;446447        /// Tokens transfer baskets448        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;449        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;450        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;451        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;452453        /// Variable metadata sponsoring454        pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Option<T::BlockNumber> = None;455456        // Contract Sponsorship and Ownership457        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;458        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;459        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;460        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;461        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 462        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 463    }464    add_extra_genesis {465        build(|config: &GenesisConfig<T>| {466            // Modification of storage467            for (_num, _c) in &config.collection {468                <Module<T>>::init_collection(_c);469            }470471            for (_num, _c, _i) in &config.nft_item_id {472                <Module<T>>::init_nft_token(*_c, _i);473            }474475            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {476                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);477            }478479            for (_num, _c, _i) in &config.refungible_item_id {480                <Module<T>>::init_refungible_token(*_c, _i);481            }482        })483    }484}485486decl_event!(487    pub enum Event<T>488    where489        AccountId = <T as system::Config>::AccountId,490    {491        /// New collection was created492        /// 493        /// # Arguments494        /// 495        /// * collection_id: Globally unique identifier of newly created collection.496        /// 497        /// * mode: [CollectionMode] converted into u8.498        /// 499        /// * account_id: Collection owner.500        Created(CollectionId, u8, AccountId),501502        /// New item was created.503        /// 504        /// # Arguments505        /// 506        /// * collection_id: Id of the collection where item was created.507        /// 508        /// * item_id: Id of an item. Unique within the collection.509        ///510        /// * recipient: Owner of newly created item 511        ItemCreated(CollectionId, TokenId, AccountId),512513        /// Collection item was burned.514        /// 515        /// # Arguments516        /// 517        /// collection_id.518        /// 519        /// item_id: Identifier of burned NFT.520        ItemDestroyed(CollectionId, TokenId),521522        /// Item was transferred523        ///524        /// * collection_id: Id of collection to which item is belong525        ///526        /// * item_id: Id of an item527        ///528        /// * sender: Original owner of item529        ///530        /// * recipient: New owner of item531        ///532        /// * amount: Always 1 for NFT533        Transfer(CollectionId, TokenId, AccountId, AccountId, u128),534    }535);536537decl_module! {538    pub struct Module<T: Config> for enum Call 539    where 540        origin: T::Origin541    {542        fn deposit_event() = default;543        type Error = Error<T>;544545        fn on_initialize(now: T::BlockNumber) -> Weight {546547            if ChainVersion::get() < 2548            {549                let value = NextCollectionID::get();550                CreatedCollectionCount::put(value);551                ChainVersion::put(2);552            }553554            0555        }556557        /// 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.558        /// 559        /// # Permissions560        /// 561        /// * Anyone.562        /// 563        /// # Arguments564        /// 565        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.566        /// 567        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.568        /// 569        /// * token_prefix: UTF-8 string with token prefix.570        /// 571        /// * mode: [CollectionMode] collection type and type dependent data.572        // returns collection ID573        #[weight = <T as Config>::WeightInfo::create_collection()]574        pub fn create_collection(origin,575                                 collection_name: Vec<u16>,576                                 collection_description: Vec<u16>,577                                 token_prefix: Vec<u8>,578                                 mode: CollectionMode) -> DispatchResult {579580            // Anyone can create a collection581            let who = ensure_signed(origin)?;582583            let decimal_points = match mode {584                CollectionMode::Fungible(points) => points,585                _ => 0586            };587588            let chain_limit = ChainLimit::get();589590            // bound Total number of collections591            ensure!(CollectionCount::get() < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);592593            // check params594            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);595            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);596            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);597            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);598599            // Generate next collection ID600            let next_id = CreatedCollectionCount::get()601                .checked_add(1)602                .ok_or(Error::<T>::NumOverflow)?;603604            // bound counter605            let total = CollectionCount::get()606                .checked_add(1)607                .ok_or(Error::<T>::NumOverflow)?;608609            CreatedCollectionCount::put(next_id);610            CollectionCount::put(total);611612            let limits = CollectionLimits {613                sponsored_data_size: chain_limit.custom_data_limit,614                ..Default::default()615            };616617            // Create new collection618            let new_collection = CollectionType {619                owner: who.clone(),620                name: collection_name,621                mode: mode.clone(),622                mint_mode: false,623                access: AccessMode::Normal,624                description: collection_description,625                decimal_points: decimal_points,626                token_prefix: token_prefix,627                offchain_schema: Vec::new(),628                schema_version: SchemaVersion::ImageURL,629                sponsor: T::AccountId::default(),630                sponsor_confirmed: false,631                variable_on_chain_schema: Vec::new(),632                const_on_chain_schema: Vec::new(),633                limits,634            };635636            // Add new collection to map637            <Collection<T>>::insert(next_id, new_collection);638639            // call event640            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));641642            Ok(())643        }644645        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.646        /// 647        /// # Permissions648        /// 649        /// * Collection Owner.650        /// 651        /// # Arguments652        /// 653        /// * collection_id: collection to destroy.654        #[weight = <T as Config>::WeightInfo::destroy_collection()]655        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {656657            let sender = ensure_signed(origin)?;658            Self::check_owner_permissions(collection_id, sender)?;659660            let target_collection = <Collection<T>>::get(collection_id);661            if !target_collection.limits.owner_can_destroy {662                fail!(Error::<T>::NoPermission);663            }664665            <AddressTokens<T>>::remove_prefix(collection_id);666            <Allowances<T>>::remove_prefix(collection_id);667            <Balance<T>>::remove_prefix(collection_id);668            <ItemListIndex>::remove(collection_id);669            <AdminList<T>>::remove(collection_id);670            <Collection<T>>::remove(collection_id);671            <WhiteList<T>>::remove_prefix(collection_id);672673            <NftItemList<T>>::remove_prefix(collection_id);674            <FungibleItemList<T>>::remove_prefix(collection_id);675            <ReFungibleItemList<T>>::remove_prefix(collection_id);676677            <NftTransferBasket<T>>::remove_prefix(collection_id);678            <FungibleTransferBasket<T>>::remove_prefix(collection_id);679            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);680681            <VariableMetaDataBasket<T>>::remove_prefix(collection_id);682683            if CollectionCount::get() > 0684            {685                // bound couter686                let total = CollectionCount::get()687                    .checked_sub(1)688                    .ok_or(Error::<T>::NumOverflow)?;689690                CollectionCount::put(total);691            }692693            Ok(())694        }695696        /// Add an address to white list.697        /// 698        /// # Permissions699        /// 700        /// * Collection Owner701        /// * Collection Admin702        /// 703        /// # Arguments704        /// 705        /// * collection_id.706        /// 707        /// * address.708        #[weight = <T as Config>::WeightInfo::add_to_white_list()]709        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{710711            let sender = ensure_signed(origin)?;712            Self::check_owner_or_admin_permissions(collection_id, sender)?;713714            <WhiteList<T>>::insert(collection_id, address, true);715            716            Ok(())717        }718719        /// Remove an address from white list.720        /// 721        /// # Permissions722        /// 723        /// * Collection Owner724        /// * Collection Admin725        /// 726        /// # Arguments727        /// 728        /// * collection_id.729        /// 730        /// * address.731        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]732        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{733734            let sender = ensure_signed(origin)?;735            Self::check_owner_or_admin_permissions(collection_id, sender)?;736737            <WhiteList<T>>::remove(collection_id, address);738739            Ok(())740        }741742        /// Toggle between normal and white list access for the methods with access for `Anyone`.743        /// 744        /// # Permissions745        /// 746        /// * Collection Owner.747        /// 748        /// # Arguments749        /// 750        /// * collection_id.751        /// 752        /// * mode: [AccessMode]753        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]754        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult755        {756            let sender = ensure_signed(origin)?;757758            Self::check_owner_permissions(collection_id, sender)?;759            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();760            target_collection.access = mode;761            <Collection<T>>::insert(collection_id, target_collection);762763            Ok(())764        }765766        /// Allows Anyone to create tokens if:767        /// * White List is enabled, and768        /// * Address is added to white list, and769        /// * This method was called with True parameter770        /// 771        /// # Permissions772        /// * Collection Owner773        ///774        /// # Arguments775        /// 776        /// * collection_id.777        /// 778        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.779        #[weight = <T as Config>::WeightInfo::set_mint_permission()]780        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult781        {782            let sender = ensure_signed(origin)?;783784            Self::check_owner_permissions(collection_id, sender)?;785            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();786            target_collection.mint_mode = mint_permission;787            <Collection<T>>::insert(collection_id, target_collection);788789            Ok(())790        }791792        /// Change the owner of the collection.793        /// 794        /// # Permissions795        /// 796        /// * Collection Owner.797        /// 798        /// # Arguments799        /// 800        /// * collection_id.801        /// 802        /// * new_owner.803        #[weight = <T as Config>::WeightInfo::change_collection_owner()]804        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {805806            let sender = ensure_signed(origin)?;807            Self::check_owner_permissions(collection_id, sender)?;808            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();809            target_collection.owner = new_owner;810            <Collection<T>>::insert(collection_id, target_collection);811812            Ok(())813        }814815        /// Adds an admin of the Collection.816        /// 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. 817        /// 818        /// # Permissions819        /// 820        /// * Collection Owner.821        /// * Collection Admin.822        /// 823        /// # Arguments824        /// 825        /// * collection_id: ID of the Collection to add admin for.826        /// 827        /// * new_admin_id: Address of new admin to add.828        #[weight = <T as Config>::WeightInfo::add_collection_admin()]829        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {830831            let sender = ensure_signed(origin)?;832            Self::check_owner_or_admin_permissions(collection_id, sender)?;833            let mut admin_arr: Vec<T::AccountId> = Vec::new();834835            if <AdminList<T>>::contains_key(collection_id)836            {837                admin_arr = <AdminList<T>>::get(collection_id);838                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);839            }840841            // Number of collection admins842            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);843844            admin_arr.push(new_admin_id);845            <AdminList<T>>::insert(collection_id, admin_arr);846847            Ok(())848        }849850        /// 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.851        ///852        /// # Permissions853        /// 854        /// * Collection Owner.855        /// * Collection Admin.856        /// 857        /// # Arguments858        /// 859        /// * collection_id: ID of the Collection to remove admin for.860        /// 861        /// * account_id: Address of admin to remove.862        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]863        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {864865            let sender = ensure_signed(origin)?;866            Self::check_owner_or_admin_permissions(collection_id, sender)?;867            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);868869            let mut admin_arr = <AdminList<T>>::get(collection_id);870            admin_arr.retain(|i| *i != account_id);871            <AdminList<T>>::insert(collection_id, admin_arr);872873            Ok(())874        }875876        /// # Permissions877        /// 878        /// * Collection Owner879        /// 880        /// # Arguments881        /// 882        /// * collection_id.883        /// 884        /// * new_sponsor.885        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]886        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {887888            let sender = ensure_signed(origin)?;889890            let mut target_collection = <Collection<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?;891            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);892893            target_collection.sponsor = new_sponsor;894            target_collection.sponsor_confirmed = false;895            <Collection<T>>::insert(collection_id, target_collection);896897            Ok(())898        }899900        /// # Permissions901        /// 902        /// * Sponsor.903        /// 904        /// # Arguments905        /// 906        /// * collection_id.907        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]908        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {909910            let sender = ensure_signed(origin)?;911912            let mut target_collection = <Collection<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?;913            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);914915            target_collection.sponsor_confirmed = true;916            <Collection<T>>::insert(collection_id, target_collection);917918            Ok(())919        }920921        /// Switch back to pay-per-own-transaction model.922        ///923        /// # Permissions924        ///925        /// * Collection owner.926        /// 927        /// # Arguments928        /// 929        /// * collection_id.930        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]931        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {932933            let sender = ensure_signed(origin)?;934935            let mut target_collection = <Collection<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?;936            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);937938            target_collection.sponsor = T::AccountId::default();939            target_collection.sponsor_confirmed = false;940            <Collection<T>>::insert(collection_id, target_collection);941942            Ok(())943        }944945        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.946        /// 947        /// # Permissions948        /// 949        /// * Collection Owner.950        /// * Collection Admin.951        /// * Anyone if952        ///     * White List is enabled, and953        ///     * Address is added to white list, and954        ///     * MintPermission is enabled (see SetMintPermission method)955        /// 956        /// # Arguments957        /// 958        /// * collection_id: ID of the collection.959        /// 960        /// * owner: Address, initial owner of the NFT.961        ///962        /// * data: Token data to store on chain.963        // #[weight =964        // (130_000_000 as Weight)965        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))966        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))967        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]968969        #[weight = <T as Config>::WeightInfo::create_item(data.len())]970        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {971972            let sender = ensure_signed(origin)?;973974            Self::collection_exists(collection_id)?;975976            let target_collection = <Collection<T>>::get(collection_id).unwrap();977978            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;979            Self::validate_create_item_args(&target_collection, &data)?;980            Self::create_item_no_validation(collection_id, owner, data)?;981982            Ok(())983        }984985        /// This method creates multiple instances of NFT Collection created with CreateCollection method.986        /// 987        /// # Permissions988        /// 989        /// * Collection Owner.990        /// * Collection Admin.991        /// * Anyone if992        ///     * White List is enabled, and993        ///     * Address is added to white list, and994        ///     * MintPermission is enabled (see SetMintPermission method)995        /// 996        /// # Arguments997        /// 998        /// * collection_id: ID of the collection.999        /// 1000        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].1001        /// 1002        /// * owner: Address, initial owner of the NFT.1003        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1004                               .map(|data| { data.len() })1005                               .sum())]1006        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {10071008            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1009            let sender = ensure_signed(origin)?;10101011            Self::collection_exists(collection_id)?;1012            let target_collection = <Collection<T>>::get(collection_id).unwrap();10131014            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;10151016            for data in &items_data {1017                Self::validate_create_item_args(&target_collection, data)?;1018            }1019            for data in &items_data {1020                Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?;1021            }10221023            Ok(())1024        }10251026        /// Destroys a concrete instance of NFT.1027        /// 1028        /// # Permissions1029        /// 1030        /// * Collection Owner.1031        /// * Collection Admin.1032        /// * Current NFT Owner.1033        /// 1034        /// # Arguments1035        /// 1036        /// * collection_id: ID of the collection.1037        /// 1038        /// * item_id: ID of NFT to burn.1039        #[weight = <T as Config>::WeightInfo::burn_item()]1040        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10411042            let sender = ensure_signed(origin)?;1043            Self::collection_exists(collection_id)?;10441045            // Transfer permissions check1046            let target_collection = <Collection<T>>::get(collection_id).unwrap();1047            ensure!(1048                Self::is_item_owner(sender.clone(), collection_id, item_id) ||1049                (1050                    target_collection.limits.owner_can_transfer &&1051                    Self::is_owner_or_admin_permissions(collection_id, sender.clone())1052                ),1053                Error::<T>::NoPermission1054            );10551056            if target_collection.access == AccessMode::WhiteList {1057                Self::check_white_list(collection_id, &sender)?;1058            }10591060            match target_collection.mode1061            {1062                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1063                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1064                CollectionMode::ReFungible  => Self::burn_refungible_item(collection_id, item_id, &sender)?,1065                _ => ()1066            };10671068            // call event1069            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10701071            Ok(())1072        }10731074        /// Change ownership of the token.1075        /// 1076        /// # Permissions1077        /// 1078        /// * Collection Owner1079        /// * Collection Admin1080        /// * Current NFT owner1081        ///1082        /// # Arguments1083        /// 1084        /// * recipient: Address of token recipient.1085        /// 1086        /// * collection_id.1087        /// 1088        /// * item_id: ID of the item1089        ///     * Non-Fungible Mode: Required.1090        ///     * Fungible Mode: Ignored.1091        ///     * Re-Fungible Mode: Required.1092        /// 1093        /// * value: Amount to transfer.1094        ///     * Non-Fungible Mode: Ignored1095        ///     * Fungible Mode: Must specify transferred amount1096        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1097        #[weight = <T as Config>::WeightInfo::transfer()]1098        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1099            let sender = ensure_signed(origin)?;1100            Self::transfer_internal(sender, recipient, collection_id, item_id, value)1101        }11021103        /// Set, change, or remove approved address to transfer the ownership of the NFT.1104        /// 1105        /// # Permissions1106        /// 1107        /// * Collection Owner1108        /// * Collection Admin1109        /// * Current NFT owner1110        /// 1111        /// # Arguments1112        /// 1113        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1114        /// 1115        /// * collection_id.1116        /// 1117        /// * item_id: ID of the item.1118        #[weight = <T as Config>::WeightInfo::approve()]1119        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {11201121            let sender = ensure_signed(origin)?;11221123            Self::collection_exists(collection_id)?;1124            Self::token_exists(collection_id, item_id, &sender)?;11251126            // Transfer permissions check1127            let target_collection = <Collection<T>>::get(collection_id).unwrap();1128            let allowance_limit = if (1129                target_collection.limits.owner_can_transfer &&1130                Self::is_owner_or_admin_permissions(1131                    collection_id,1132                    sender.clone(),1133                )1134            ) {1135                None1136            } else if let Some(amount) = Self::owned_amount(1137                sender.clone(),1138                collection_id,1139                item_id,1140            ) {1141                Some(amount)1142            } else {1143                fail!(Error::<T>::NoPermission);1144            };11451146            if target_collection.access == AccessMode::WhiteList {1147                Self::check_white_list(collection_id, &sender)?;1148                Self::check_white_list(collection_id, &spender)?;1149            }11501151            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1152            let mut allowance: u128 = amount;1153            if allowance_exists {1154                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1155            }1156            if let Some(limit) = allowance_limit {1157                ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1158            }1159            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11601161            Ok(())1162        }1163        1164        /// 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.1165        /// 1166        /// # Permissions1167        /// * Collection Owner1168        /// * Collection Admin1169        /// * Current NFT owner1170        /// * Address approved by current NFT owner1171        /// 1172        /// # Arguments1173        /// 1174        /// * from: Address that owns token.1175        /// 1176        /// * recipient: Address of token recipient.1177        /// 1178        /// * collection_id.1179        /// 1180        /// * item_id: ID of the item.1181        /// 1182        /// * value: Amount to transfer.1183        #[weight = <T as Config>::WeightInfo::transfer_from()]1184        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11851186            let sender = ensure_signed(origin)?;1187            let mut appoved_transfer = false;11881189            // Check approval1190            let mut approval: u128 = 0;1191            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1192                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1193                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1194                appoved_transfer = true;1195            }11961197            let target_collection = <Collection<T>>::get(collection_id).unwrap();11981199            // Limits check1200            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;12011202            // Transfer permissions check         1203            ensure!(1204                appoved_transfer || 1205                (1206                    target_collection.limits.owner_can_transfer &&1207                    Self::is_owner_or_admin_permissions(collection_id, sender.clone())1208                ),1209                Error::<T>::NoPermission1210            );12111212            if target_collection.access == AccessMode::WhiteList {1213                Self::check_white_list(collection_id, &sender)?;1214                Self::check_white_list(collection_id, &recipient)?;1215            }12161217            // Reduce approval by transferred amount or remove if remaining approval drops to 01218            if approval.checked_sub(value).unwrap_or(0) > 0 {1219                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1220            }1221            else {1222                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1223            }12241225            match target_collection.mode1226            {1227                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1228                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1229                CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1230                _ => ()1231            };12321233            Ok(())1234        }12351236        // #[weight = 0]1237        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {12381239        //     // let no_perm_mes = "You do not have permissions to modify this collection";1240        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1241        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1242        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);12431244        //     // // on_nft_received  call12451246        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;12471248        //     Ok(())1249        // }12501251        /// Set off-chain data schema.1252        /// 1253        /// # Permissions1254        /// 1255        /// * Collection Owner1256        /// * Collection Admin1257        /// 1258        /// # Arguments1259        /// 1260        /// * collection_id.1261        /// 1262        /// * schema: String representing the offchain data schema.1263        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1264        pub fn set_variable_meta_data (1265            origin,1266            collection_id: CollectionId,1267            item_id: TokenId,1268            data: Vec<u8>1269        ) -> DispatchResult {1270            let sender = ensure_signed(origin)?;1271            1272            Self::collection_exists(collection_id)?;1273            Self::token_exists(collection_id, item_id, &sender)?;12741275            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12761277            // Modify permissions check1278            let target_collection = <Collection<T>>::get(collection_id).unwrap();1279            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1280                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1281                Error::<T>::NoPermission);12821283            match target_collection.mode1284            {1285                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1286                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1287                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1288                _ => fail!(Error::<T>::UnexpectedCollectionType)1289            };12901291            Ok(())1292        }1293 1294        /// Set schema standard1295        /// ImageURL1296        /// Unique1297        /// 1298        /// # Permissions1299        /// 1300        /// * Collection Owner1301        /// * Collection Admin1302        /// 1303        /// # Arguments1304        /// 1305        /// * collection_id.1306        /// 1307        /// * schema: SchemaVersion: enum1308        #[weight = <T as Config>::WeightInfo::set_schema_version()]1309        pub fn set_schema_version(1310            origin,1311            collection_id: CollectionId,1312            version: SchemaVersion1313        ) -> DispatchResult {1314            let sender = ensure_signed(origin)?;1315            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1316            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();1317            target_collection.schema_version = version;1318            <Collection<T>>::insert(collection_id, target_collection);13191320            Ok(())1321        }13221323        /// Set off-chain data schema.1324        /// 1325        /// # Permissions1326        /// 1327        /// * Collection Owner1328        /// * Collection Admin1329        /// 1330        /// # Arguments1331        /// 1332        /// * collection_id.1333        /// 1334        /// * schema: String representing the offchain data schema.1335        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1336        pub fn set_offchain_schema(1337            origin,1338            collection_id: CollectionId,1339            schema: Vec<u8>1340        ) -> DispatchResult {1341            let sender = ensure_signed(origin)?;1342            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13431344            // check schema limit1345            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");13461347            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();1348            target_collection.offchain_schema = schema;1349            <Collection<T>>::insert(collection_id, target_collection);13501351            Ok(())1352        }13531354        /// Set const on-chain data schema.1355        /// 1356        /// # Permissions1357        /// 1358        /// * Collection Owner1359        /// * Collection Admin1360        /// 1361        /// # Arguments1362        /// 1363        /// * collection_id.1364        /// 1365        /// * schema: String representing the const on-chain data schema.1366        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1367        pub fn set_const_on_chain_schema (1368            origin,1369            collection_id: CollectionId,1370            schema: Vec<u8>1371        ) -> DispatchResult {1372            let sender = ensure_signed(origin)?;1373            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13741375            // check schema limit1376            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");13771378            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();1379            target_collection.const_on_chain_schema = schema;1380            <Collection<T>>::insert(collection_id, target_collection);13811382            Ok(())1383        }13841385        /// Set variable on-chain data schema.1386        /// 1387        /// # Permissions1388        /// 1389        /// * Collection Owner1390        /// * Collection Admin1391        /// 1392        /// # Arguments1393        /// 1394        /// * collection_id.1395        /// 1396        /// * schema: String representing the variable on-chain data schema.1397        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1398        pub fn set_variable_on_chain_schema (1399            origin,1400            collection_id: CollectionId,1401            schema: Vec<u8>1402        ) -> DispatchResult {1403            let sender = ensure_signed(origin)?;1404            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;14051406            // check schema limit1407            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14081409            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();1410            target_collection.variable_on_chain_schema = schema;1411            <Collection<T>>::insert(collection_id, target_collection);14121413            Ok(())1414        }14151416        // Sudo permissions function1417        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1418        pub fn set_chain_limits(1419            origin,1420            limits: ChainLimits1421        ) -> DispatchResult {14221423            #[cfg(not(feature = "runtime-benchmarks"))]1424            ensure_root(origin)?;14251426            <ChainLimit>::put(limits);1427            Ok(())1428        }14291430        /// Enable smart contract self-sponsoring.1431        /// 1432        /// # Permissions1433        /// 1434        /// * Contract Owner1435        /// 1436        /// # Arguments1437        /// 1438        /// * contract address1439        /// * enable flag1440        /// 1441        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1442        pub fn enable_contract_sponsoring(1443            origin,1444            contract_address: T::AccountId,1445            enable: bool1446        ) -> DispatchResult {14471448            let sender = ensure_signed(origin)?;14491450            #[cfg(feature = "runtime-benchmarks")]1451            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14521453            Self::ensure_contract_owned(sender, &contract_address)?;14541455            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1456            Ok(())1457        }14581459        /// Set the rate limit for contract sponsoring to specified number of blocks.1460        /// 1461        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1462        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1463        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1464        /// from contract endowment if there are at least B blocks between such transactions. 1465        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1466        /// 1467        /// # Permissions1468        /// 1469        /// * Contract Owner1470        /// 1471        /// # Arguments1472        /// 1473        /// -`contract_address`: Address of the contract to sponsor1474        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1475        /// 1476        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1477        pub fn set_contract_sponsoring_rate_limit(1478            origin,1479            contract_address: T::AccountId,1480            rate_limit: T::BlockNumber1481        ) -> DispatchResult {1482            let sender = ensure_signed(origin)?;14831484            #[cfg(feature = "runtime-benchmarks")]1485            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14861487            Self::ensure_contract_owned(sender, &contract_address)?;1488            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1489            Ok(())1490        }14911492        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1493        /// 1494        /// # Permissions1495        /// 1496        /// * Address that deployed smart contract.1497        /// 1498        /// # Arguments1499        /// 1500        /// -`contract_address`: Address of the contract.1501        /// 1502        /// - `enable`: .  1503        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1504        pub fn toggle_contract_white_list(1505            origin,1506            contract_address: T::AccountId,1507            enable: bool1508        ) -> DispatchResult {1509            let sender = ensure_signed(origin)?;15101511            #[cfg(feature = "runtime-benchmarks")]1512            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15131514            Self::ensure_contract_owned(sender, &contract_address)?;1515            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1516            Ok(())1517        }1518        1519        /// Add an address to smart contract white list.1520        /// 1521        /// # Permissions1522        /// 1523        /// * Address that deployed smart contract.1524        /// 1525        /// # Arguments1526        /// 1527        /// -`contract_address`: Address of the contract.1528        ///1529        /// -`account_address`: Address to add.1530        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1531        pub fn add_to_contract_white_list(1532            origin,1533            contract_address: T::AccountId,1534            account_address: T::AccountId1535        ) -> DispatchResult {1536            let sender = ensure_signed(origin)?;15371538            #[cfg(feature = "runtime-benchmarks")]1539            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1540            1541            Self::ensure_contract_owned(sender, &contract_address)?;      1542            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1543            Ok(())1544        }15451546        /// Remove an address from smart contract white list.1547        /// 1548        /// # Permissions1549        /// 1550        /// * Address that deployed smart contract.1551        /// 1552        /// # Arguments1553        /// 1554        /// -`contract_address`: Address of the contract.1555        ///1556        /// -`account_address`: Address to remove.1557        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1558        pub fn remove_from_contract_white_list(1559            origin,1560            contract_address: T::AccountId,1561            account_address: T::AccountId1562        ) -> DispatchResult {1563            let sender = ensure_signed(origin)?;15641565            #[cfg(feature = "runtime-benchmarks")]1566            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15671568            Self::ensure_contract_owned(sender, &contract_address)?;1569            <ContractWhiteList<T>>::remove(contract_address, account_address);1570            Ok(())1571        }15721573        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1574        pub fn set_collection_limits(1575            origin,1576            collection_id: u32,1577            new_limits: CollectionLimits<T::BlockNumber>,1578        ) -> DispatchResult {1579            let sender = ensure_signed(origin)?;1580            Self::check_owner_permissions(collection_id, sender.clone())?;1581            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();1582            let old_limits = target_collection.limits;1583            let chain_limits = ChainLimit::get();15841585            // collection bounds1586            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1587                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1588                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1589                Error::<T>::CollectionLimitBoundsExceeded);15901591            // token_limit   check  prev1592            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1593            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);15941595            ensure!(1596                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1597                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1598                Error::<T>::OwnerPermissionsCantBeReverted,1599            );16001601            target_collection.limits = new_limits;1602            <Collection<T>>::insert(collection_id, target_collection);16031604            Ok(())1605        } 1606    }1607}16081609impl<T: Config> Module<T> {16101611    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {16121613        let target_collection = <Collection<T>>::get(collection_id).unwrap();16141615        // Limits check1616        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;16171618        // Transfer permissions check1619        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1620            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1621            Error::<T>::NoPermission);16221623        if target_collection.access == AccessMode::WhiteList {1624            Self::check_white_list(collection_id, &sender)?;1625            Self::check_white_list(collection_id, &recipient)?;1626        }16271628        match target_collection.mode1629        {1630            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient.clone())?,1631            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1632            CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient.clone())?,1633            _ => ()1634        };16351636        Self::deposit_event(RawEvent::Transfer(collection_id, item_id, sender, recipient, value));16371638        Ok(())1639    }164016411642    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T>, recipient: &T::AccountId) -> DispatchResult {16431644        // check token limit and account token limit1645        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1646        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1647        1648        Ok(())1649    }16501651    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {16521653        // check token limit and account token limit1654        let total_items: u32 = ItemListIndex::get(collection_id);1655        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1656        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1657        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);16581659        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1660            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1661            Self::check_white_list(collection_id, owner)?;1662            Self::check_white_list(collection_id, sender)?;1663        }16641665        Ok(())1666    }16671668    fn validate_create_item_args(target_collection: &CollectionType<T>, data: &CreateItemData) -> DispatchResult {1669        match target_collection.mode1670        {1671            CollectionMode::NFT => {1672                if let CreateItemData::NFT(data) = data {1673                    // check sizes1674                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1675                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1676                } else {1677                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1678                }1679            },1680            CollectionMode::Fungible(_) => {1681                if let CreateItemData::Fungible(_) = data {1682                } else {1683                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1684                }1685            },1686            CollectionMode::ReFungible => {1687                if let CreateItemData::ReFungible(data) = data {16881689                    // check sizes1690                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1691                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16921693                    // Check refungibility limits1694                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1695                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1696                } else {1697                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1698                }1699            },1700            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1701        };17021703        Ok(())1704    }17051706    fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1707        match data1708        {1709            CreateItemData::NFT(data) => {1710                let item = NftItemType {1711                    owner: owner.clone(),1712                    const_data: data.const_data,1713                    variable_data: data.variable_data1714                };17151716                Self::add_nft_item(collection_id, item)?;1717            },1718            CreateItemData::Fungible(data) => {1719                Self::add_fungible_item(collection_id, &owner, data.value)?;1720            },1721            CreateItemData::ReFungible(data) => {1722                let mut owner_list = Vec::new();1723                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});17241725                let item = ReFungibleItemType {1726                    owner: owner_list,1727                    const_data: data.const_data,1728                    variable_data: data.variable_data1729                };17301731                Self::add_refungible_item(collection_id, item)?;1732            }1733        };17341735        // call event1736        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id), owner));17371738        Ok(())1739    }17401741    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {17421743        // Does new owner already have an account?1744        let mut balance: u128 = 0;1745        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1746            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1747        } 17481749        // Mint 1750        let item = FungibleItemType {1751            value: balance + value1752        };1753        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);17541755        // Update balance1756        let new_balance = <Balance<T>>::get(collection_id, owner)1757            .checked_add(value)1758            .ok_or(Error::<T>::NumOverflow)?;1759        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17601761        Ok(())1762    }17631764    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1765        let current_index = <ItemListIndex>::get(collection_id)1766            .checked_add(1)1767            .ok_or(Error::<T>::NumOverflow)?;1768        let itemcopy = item.clone();17691770        let value = item.owner.first().unwrap().fraction;1771        let owner = item.owner.first().unwrap().owner.clone();17721773        Self::add_token_index(collection_id, current_index, &owner)?;17741775        <ItemListIndex>::insert(collection_id, current_index);1776        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17771778        // Update balance1779        let new_balance = <Balance<T>>::get(collection_id, &owner)1780            .checked_add(value)1781            .ok_or(Error::<T>::NumOverflow)?;1782        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17831784        Ok(())1785    }17861787    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1788        let current_index = <ItemListIndex>::get(collection_id)1789            .checked_add(1)1790            .ok_or(Error::<T>::NumOverflow)?;17911792        let item_owner = item.owner.clone();1793        Self::add_token_index(collection_id, current_index, &item.owner)?;17941795        <ItemListIndex>::insert(collection_id, current_index);1796        <NftItemList<T>>::insert(collection_id, current_index, item);17971798        // Update balance1799        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1800            .checked_add(1)1801            .ok_or(Error::<T>::NumOverflow)?;1802        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);18031804        Ok(())1805    }18061807    fn burn_refungible_item(1808        collection_id: CollectionId,1809        item_id: TokenId,1810        owner: &T::AccountId,1811    ) -> DispatchResult {1812        ensure!(1813            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1814            Error::<T>::TokenNotFound1815        );1816        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1817        let rft_balance = token1818            .owner1819            .iter()1820            .filter(|&i| i.owner == *owner)1821            .next()1822            .unwrap();1823        Self::remove_token_index(collection_id, item_id, owner)?;18241825        // update balance1826        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1827            .checked_sub(rft_balance.fraction)1828            .ok_or(Error::<T>::NumOverflow)?;1829        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);18301831        // Re-create owners list with sender removed1832        let index = token1833            .owner1834            .iter()1835            .position(|i| i.owner == *owner)1836            .unwrap();1837        token.owner.remove(index);1838        let owner_count = token.owner.len();18391840        // Burn the token completely if this was the last (only) owner1841        if owner_count == 0 {1842            <ReFungibleItemList<T>>::remove(collection_id, item_id);1843            <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1844        }1845        else {1846            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1847        }18481849        Ok(())1850    }18511852    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1853        ensure!(1854            <NftItemList<T>>::contains_key(collection_id, item_id),1855            Error::<T>::TokenNotFound1856        );1857        let item = <NftItemList<T>>::get(collection_id, item_id);1858        Self::remove_token_index(collection_id, item_id, &item.owner)?;18591860        // update balance1861        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1862            .checked_sub(1)1863            .ok_or(Error::<T>::NumOverflow)?;1864        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1865        <NftItemList<T>>::remove(collection_id, item_id);1866        <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18671868        Ok(())1869    }18701871    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1872        ensure!(1873            <FungibleItemList<T>>::contains_key(collection_id, owner),1874            Error::<T>::TokenNotFound1875        );1876        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1877        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18781879        // update balance1880        let new_balance = <Balance<T>>::get(collection_id, owner)1881            .checked_sub(value)1882            .ok_or(Error::<T>::NumOverflow)?;1883        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18841885        if balance.value - value > 0 {1886            balance.value -= value;1887            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1888        }1889        else {1890            <FungibleItemList<T>>::remove(collection_id, owner);1891        }18921893        Ok(())1894    }18951896    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1897        ensure!(1898            <Collection<T>>::contains_key(collection_id),1899            Error::<T>::CollectionNotFound1900        );1901        Ok(())1902    }19031904    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1905        Self::collection_exists(collection_id)?;19061907        let target_collection = <Collection<T>>::get(collection_id).unwrap();1908        ensure!(1909            subject == target_collection.owner,1910            Error::<T>::NoPermission1911        );19121913        Ok(())1914    }19151916    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1917        let target_collection = <Collection<T>>::get(collection_id).unwrap();1918        let mut result: bool = subject == target_collection.owner;1919        let exists = <AdminList<T>>::contains_key(collection_id);19201921        if !result & exists {1922            if <AdminList<T>>::get(collection_id).contains(&subject) {1923                result = true1924            }1925        }19261927        result1928    }19291930    fn check_owner_or_admin_permissions(1931        collection_id: CollectionId,1932        subject: T::AccountId,1933    ) -> DispatchResult {1934        Self::collection_exists(collection_id)?;1935        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());19361937        ensure!(1938            result,1939            Error::<T>::NoPermission1940        );1941        Ok(())1942    }19431944    fn owned_amount(1945        subject: T::AccountId,1946        collection_id: CollectionId,1947        item_id: TokenId,1948    ) -> Option<u128> {1949        let target_collection = <Collection<T>>::get(collection_id);19501951        match target_collection.mode {1952            CollectionMode::NFT => {1953                if <NftItemList<T>>::get(collection_id, item_id).owner == subject {1954                    return Some(1)1955                }1956                None1957            },1958            CollectionMode::Fungible(_) => {1959                if <FungibleItemList<T>>::contains_key(collection_id, &subject) {1960                    return Some(<FungibleItemList<T>>::get(collection_id, &subject)1961                        .value);1962                }1963                None1964            },1965            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)1966                .owner1967                .iter()1968                .find(|i| i.owner == subject)1969                .map(|i| i.fraction),1970            CollectionMode::Invalid => None,1971        }1972    }19731974    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1975        let target_collection = <Collection<T>>::get(collection_id).unwrap();19761977        match target_collection.mode {1978            CollectionMode::NFT => {1979                <NftItemList<T>>::get(collection_id, item_id).owner == subject1980            }1981            CollectionMode::Fungible(_) => {1982                <FungibleItemList<T>>::contains_key(collection_id, &subject)1983            }1984            CollectionMode::ReFungible => {1985                <ReFungibleItemList<T>>::get(collection_id, item_id)1986                    .owner1987                    .iter()1988                    .any(|i| i.owner == subject)1989            }1990            CollectionMode::Invalid => false,1991        }1992    }19931994    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1995        let mes = Error::<T>::AddresNotInWhiteList;1996        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);19971998        Ok(())1999    }20002001    /// Check if token exists. In case of Fungible, check if there is an entry for 2002    /// the owner in fungible balances double map2003    fn token_exists(2004        collection_id: CollectionId,2005        item_id: TokenId,2006        owner: &T::AccountId2007    ) -> DispatchResult {2008        let target_collection = <Collection<T>>::get(collection_id).unwrap();2009        let exists = match target_collection.mode2010        {2011            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2012            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),2013            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2014            _ => false2015        };20162017        ensure!(exists == true, Error::<T>::TokenNotFound);2018        Ok(())2019    }20202021    fn transfer_fungible(2022        collection_id: CollectionId,2023        value: u128,2024        owner: &T::AccountId,2025        recipient: &T::AccountId,2026    ) -> DispatchResult {2027        Self::token_exists(collection_id, 0, owner)?;20282029        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);2030        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);20312032        // Send balance to recipient (updates balanceOf of recipient)2033        Self::add_fungible_item(collection_id, recipient, value)?;20342035        // update balanceOf of sender2036        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);20372038        // Reduce or remove sender2039        if balance.value == value {2040            <FungibleItemList<T>>::remove(collection_id, owner);2041        }2042        else {2043            balance.value -= value;2044            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);2045        }20462047        Ok(())2048    }20492050    fn transfer_refungible(2051        collection_id: CollectionId,2052        item_id: TokenId,2053        value: u128,2054        owner: T::AccountId,2055        new_owner: T::AccountId,2056    ) -> DispatchResult {2057        Self::token_exists(collection_id, item_id, &owner)?;20582059        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);2060        let item = full_item2061            .owner2062            .iter()2063            .filter(|i| i.owner == owner)2064            .next()2065            .ok_or(Error::<T>::NumOverflow)?;2066        let amount = item.fraction;20672068        ensure!(amount >= value, Error::<T>::TokenValueTooLow);20692070        // update balance2071        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2072            .checked_sub(value)2073            .ok_or(Error::<T>::NumOverflow)?;2074        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20752076        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2077            .checked_add(value)2078            .ok_or(Error::<T>::NumOverflow)?;2079        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20802081        let old_owner = item.owner.clone();2082        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20832084        // transfer2085        if amount == value && !new_owner_has_account {2086            // change owner2087            // new owner do not have account2088            let mut new_full_item = full_item.clone();2089            new_full_item2090                .owner2091                .iter_mut()2092                .find(|i| i.owner == owner)2093                .unwrap()2094                .owner = new_owner.clone();2095            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20962097            // update index collection2098            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2099        } else {2100            let mut new_full_item = full_item.clone();2101            new_full_item2102                .owner2103                .iter_mut()2104                .find(|i| i.owner == owner)2105                .unwrap()2106                .fraction -= value;21072108            // separate amount2109            if new_owner_has_account {2110                // new owner has account2111                new_full_item2112                    .owner2113                    .iter_mut()2114                    .find(|i| i.owner == new_owner)2115                    .unwrap()2116                    .fraction += value;2117            } else {2118                // new owner do not have account2119                new_full_item.owner.push(Ownership {2120                    owner: new_owner.clone(),2121                    fraction: value,2122                });2123                Self::add_token_index(collection_id, item_id, &new_owner)?;2124            }21252126            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2127        }21282129        Ok(())2130    }21312132    fn transfer_nft(2133        collection_id: CollectionId,2134        item_id: TokenId,2135        sender: T::AccountId,2136        new_owner: T::AccountId,2137    ) -> DispatchResult {2138        Self::token_exists(collection_id, item_id, &sender)?;21392140        let mut item = <NftItemList<T>>::get(collection_id, item_id);21412142        ensure!(2143            sender == item.owner,2144            Error::<T>::MustBeTokenOwner2145        );21462147        // update balance2148        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2149            .checked_sub(1)2150            .ok_or(Error::<T>::NumOverflow)?;2151        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);21522153        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2154            .checked_add(1)2155            .ok_or(Error::<T>::NumOverflow)?;2156        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);21572158        // change owner2159        let old_owner = item.owner.clone();2160        item.owner = new_owner.clone();2161        <NftItemList<T>>::insert(collection_id, item_id, item);21622163        // update index collection2164        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21652166        Ok(())2167    }2168    2169    fn set_re_fungible_variable_data(2170        collection_id: CollectionId,2171        item_id: TokenId,2172        data: Vec<u8>2173    ) -> DispatchResult {2174        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);21752176        item.variable_data = data;21772178        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21792180        Ok(())2181    }21822183    fn set_nft_variable_data(2184        collection_id: CollectionId,2185        item_id: TokenId,2186        data: Vec<u8>2187    ) -> DispatchResult {2188        let mut item = <NftItemList<T>>::get(collection_id, item_id);2189        2190        item.variable_data = data;21912192        <NftItemList<T>>::insert(collection_id, item_id, item);2193        2194        Ok(())2195    }21962197    fn init_collection(item: &CollectionType<T>) {2198        // check params2199        assert!(2200            item.decimal_points <= MAX_DECIMAL_POINTS,2201            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2202        );2203        assert!(2204            item.name.len() <= 64,2205            "Collection name can not be longer than 63 char"2206        );2207        assert!(2208            item.name.len() <= 256,2209            "Collection description can not be longer than 255 char"2210        );2211        assert!(2212            item.token_prefix.len() <= 16,2213            "Token prefix can not be longer than 15 char"2214        );22152216        // Generate next collection ID2217        let next_id = CreatedCollectionCount::get()2218            .checked_add(1)2219            .unwrap();22202221        CreatedCollectionCount::put(next_id);2222    }22232224    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2225        let current_index = <ItemListIndex>::get(collection_id)2226            .checked_add(1)2227            .unwrap();22282229        let item_owner = item.owner.clone();2230        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22312232        <ItemListIndex>::insert(collection_id, current_index);22332234        // Update balance2235        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2236            .checked_add(1)2237            .unwrap();2238        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2239    }22402241    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2242        let current_index = <ItemListIndex>::get(collection_id)2243            .checked_add(1)2244            .unwrap();22452246        Self::add_token_index(collection_id, current_index, owner).unwrap();22472248        <ItemListIndex>::insert(collection_id, current_index);22492250        // Update balance2251        let new_balance = <Balance<T>>::get(collection_id, owner)2252            .checked_add(item.value)2253            .unwrap();2254        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2255    }22562257    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2258        let current_index = <ItemListIndex>::get(collection_id)2259            .checked_add(1)2260            .unwrap();22612262        let value = item.owner.first().unwrap().fraction;2263        let owner = item.owner.first().unwrap().owner.clone();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)2271            .checked_add(value)2272            .unwrap();2273        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2274    }22752276    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {22772278        // add to account limit2279        if <AccountItemCount<T>>::contains_key(owner) {22802281            // bound Owned tokens by a single address2282            let count = <AccountItemCount<T>>::get(owner);2283            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);22842285            <AccountItemCount<T>>::insert(owner.clone(), count2286                .checked_add(1)2287                .ok_or(Error::<T>::NumOverflow)?);2288        }2289        else {2290            <AccountItemCount<T>>::insert(owner.clone(), 1);2291        }22922293        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2294        if list_exists {2295            let mut list = <AddressTokens<T>>::get(collection_id, owner);2296            let item_contains = list.contains(&item_index.clone());22972298            if !item_contains {2299                list.push(item_index.clone());2300            }23012302            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2303        } else {2304            let mut itm = Vec::new();2305            itm.push(item_index.clone());2306            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2307        }23082309        Ok(())2310    }23112312    fn remove_token_index(2313        collection_id: CollectionId,2314        item_index: TokenId,2315        owner: &T::AccountId,2316    ) -> DispatchResult {23172318        // update counter2319        <AccountItemCount<T>>::insert(owner.clone(), 2320            <AccountItemCount<T>>::get(owner)2321            .checked_sub(1)2322            .ok_or(Error::<T>::NumOverflow)?);232323242325        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2326        if list_exists {2327            let mut list = <AddressTokens<T>>::get(collection_id, owner);2328            let item_contains = list.contains(&item_index.clone());23292330            if item_contains {2331                list.retain(|&item| item != item_index);2332                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2333            }2334        }23352336        Ok(())2337    }23382339    fn move_token_index(2340        collection_id: CollectionId,2341        item_index: TokenId,2342        old_owner: &T::AccountId,2343        new_owner: &T::AccountId,2344    ) -> DispatchResult {2345        Self::remove_token_index(collection_id, item_index, old_owner)?;2346        Self::add_token_index(collection_id, item_index, new_owner)?;23472348        Ok(())2349    }2350    2351    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2352        if <ContractOwner<T>>::contains_key(contract.clone()) {2353            let owner = <ContractOwner<T>>::get(contract);2354            ensure!(account == owner, Error::<T>::NoPermission);2355        } else {2356            fail!(Error::<T>::NoPermission);2357        }23582359        Ok(())2360    }2361}23622363////////////////////////////////////////////////////////////////////////////////////////////////////2364// Economic models2365// #region23662367/// Fee multiplier.2368pub type Multiplier = FixedU128;23692370type BalanceOf<T> = <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::Balance;23712372/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2373/// in the queue.2374#[derive(Encode, Decode, Clone, Eq, PartialEq)]2375pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);23762377impl<T: Config + Send + Sync> sp_std::fmt::Debug 2378    for ChargeTransactionPayment<T>2379{2380	#[cfg(feature = "std")]2381	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2382		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2383	}2384	#[cfg(not(feature = "std"))]2385	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2386		Ok(())2387	}2388}23892390impl<T: Config> ChargeTransactionPayment<T>2391where2392    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2393    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2394    T::AccountId: AsRef<[u8]>,2395    T::AccountId: UncheckedFrom<T::Hash>,2396{2397    fn traditional_fee(2398        len: usize,2399        info: &DispatchInfoOf<T::Call>,2400        tip: BalanceOf<T>,2401    ) -> BalanceOf<T>2402    where2403        T::Call: Dispatchable<Info = DispatchInfo>,2404    {2405        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2406    }24072408	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2409        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2410        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2411        let len_saturation = max_block_length as u64 / (len as u64).max(1);2412        let coefficient: BalanceOf<T> = weight_saturation2413            .min(len_saturation)2414            .saturated_into::<BalanceOf<T>>();2415        final_fee2416            .saturating_mul(coefficient)2417            .saturated_into::<TransactionPriority>()2418    }24192420    fn withdraw_fee(2421        &self,2422        who: &T::AccountId,2423        call: &T::Call,2424        info: &DispatchInfoOf<T::Call>,2425        len: usize,2426	) -> Result<2427		(2428			BalanceOf<T>,2429			<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2430		),2431		TransactionValidityError,2432	> {2433        let tip = self.0;24342435        // Set fee based on call type. Creating collection costs 1 Unique.2436        // All other transactions have traditional fees so far2437        // let fee = match call.is_sub_type() {2438        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2439        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2440        //                                                 // _ => <BalanceOf<T>>::from(100)2441        // };2442        let fee = Self::traditional_fee(len, info, tip);24432444        // Only mess with balances if fee is not zero.2445        if fee.is_zero() {2446            return <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2447			.map(|i| (fee, i));2448        }24492450        // Determine who is paying transaction fee based on ecnomic model2451        // Parse call to extract collection ID and access collection sponsor2452        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2453            Some(Call::create_item(collection_id, _owner, _properties)) => {2454                let collection = <Collection<T>>::get(collection_id).unwrap();24552456                // sponsor timeout2457                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;24582459                let limit = collection.limits.sponsor_transfer_timeout;2460                let mut sponsored = true;2461                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2462                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2463                    let limit_time = last_tx_block + limit.into();2464                    if block_number <= limit_time {2465                        sponsored = false;2466                    }2467                }2468                if sponsored {2469                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2470                }24712472                // check free create limit2473                if (collection.limits.sponsored_data_size >= (_properties.len() as u32)) &&2474                   (collection.sponsor_confirmed) &&2475                   (sponsored)2476                {2477                    collection.sponsor2478                } else {2479                    T::AccountId::default()2480                }2481            }2482            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2483                let collection = <Collection<T>>::get(collection_id).unwrap();2484                2485                let mut sponsor_transfer = false;2486                if collection.sponsor_confirmed {24872488                    let collection_limits = collection.limits;2489                    let collection_mode = collection.mode;2490    2491                    // sponsor timeout2492                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2493                    sponsor_transfer = match collection_mode {2494                        CollectionMode::NFT => {2495    2496                            // get correct limit2497                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2498                                collection_limits.sponsor_transfer_timeout2499                            } else {2500                                ChainLimit::get().nft_sponsor_transfer_timeout2501                            };2502    2503                            let mut sponsored = true;2504                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2505                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2506                                let limit_time = last_tx_block + limit.into();2507                                if block_number <= limit_time {2508                                    sponsored = false;2509                                }2510                            }2511                            if sponsored {2512                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2513                            }25142515                            sponsored2516                        }2517                        CollectionMode::Fungible(_) => {2518    2519                            // get correct limit2520                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2521                                collection_limits.sponsor_transfer_timeout2522                            } else {2523                                ChainLimit::get().fungible_sponsor_transfer_timeout2524                            };2525    2526                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2527                            let mut sponsored = true;2528                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2529                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2530                                let limit_time = last_tx_block + limit.into();2531                                if block_number <= limit_time {2532                                    sponsored = false;2533                                }2534                            }2535                            if sponsored {2536                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2537                            }25382539                            sponsored2540                        }2541                        CollectionMode::ReFungible => {2542    2543                            // get correct limit2544                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2545                                collection_limits.sponsor_transfer_timeout2546                            } else {2547                                ChainLimit::get().refungible_sponsor_transfer_timeout2548                            };2549    2550                            let mut sponsored = true;2551                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2552                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2553                                let limit_time = last_tx_block + limit.into();2554                                if block_number <= limit_time {2555                                    sponsored = false;2556                                }2557                            }2558                            if sponsored {2559                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2560                            }25612562                            sponsored2563                        }2564                        _ => {2565                            false2566                        },2567                    };2568                }25692570                if !sponsor_transfer {2571                    T::AccountId::default()2572                } else {2573                    collection.sponsor2574                }2575            }25762577            Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2578                let mut sponsor_metadata_changes = false;25792580                let collection = <Collection<T>>::get(collection_id).unwrap();25812582                if2583                    collection.sponsor_confirmed &&2584                    // Can't sponsor fungible collection, this tx will be rejected2585                    // as invalid2586                    !matches!(collection.mode, CollectionMode::Fungible(_)) &&2587                    data.len() <= collection.limits.sponsored_data_size as usize2588                {2589                    if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2590                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;25912592                        if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2593                            .map(|last_block| block_number - last_block > rate_limit)2594                            .unwrap_or(true) 2595                        {2596                            sponsor_metadata_changes = true;2597                            <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2598                        }2599                    }2600                }26012602                if !sponsor_metadata_changes {2603                    T::AccountId::default()2604                } else {2605                    collection.sponsor2606                }2607            }26082609            _ => T::AccountId::default(),2610        };26112612        // Sponsor smart contracts2613        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {26142615            // On instantiation: set the contract owner2616            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {26172618                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2619                    &who,2620                    code_hash,2621                    salt,2622                );2623                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());26242625                T::AccountId::default()2626            },26272628            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2629            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {26302631                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());26322633                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2634                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2635                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2636                  2637                if !owned_contract && white_list_enabled {2638                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2639                        return Err(InvalidTransaction::Call.into());2640                    }2641                }26422643                let mut sponsor_transfer = false;2644                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2645                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2646                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2647                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2648                    let limit_time = last_tx_block + rate_limit;26492650                    if block_number >= limit_time {2651                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2652                        sponsor_transfer = true;2653                    }2654                } else {2655                    sponsor_transfer = false;2656                }2657               2658                2659                let mut sp = T::AccountId::default();2660                if sponsor_transfer {2661                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2662                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2663                            sp = called_contract;2664                        }2665                    }2666                }26672668                sp2669            },26702671            _ => sponsor,2672        };26732674        let mut who_pays_fee: T::AccountId = sponsor.clone();2675        if sponsor == T::AccountId::default() {2676            who_pays_fee = who.clone();2677        }26782679		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2680			.map(|i| (fee, i))2681    }2682}268326842685impl<T: Config + Send + Sync> SignedExtension2686    for ChargeTransactionPayment<T>2687where2688    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2689    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2690    T::AccountId: AsRef<[u8]>,2691    T::AccountId: UncheckedFrom<T::Hash>,2692{2693    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2694    type AccountId = T::AccountId;2695    type Call = T::Call;2696    type AdditionalSigned = ();2697    type Pre = (2698        // tip2699        BalanceOf<T>,2700        // who pays fee2701        Self::AccountId,2702		// imbalance resulting from withdrawing the fee2703		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2704    );2705    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2706        Ok(())2707    }27082709    fn validate(2710        &self,2711        who: &Self::AccountId,2712        call: &Self::Call,2713        info: &DispatchInfoOf<Self::Call>,2714        len: usize,2715    ) -> TransactionValidity {2716		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2717		Ok(ValidTransaction {2718			priority: Self::get_priority(len, info, fee),2719			..Default::default()2720		})2721    }27222723    fn pre_dispatch(2724        self,2725        who: &Self::AccountId,2726        call: &Self::Call,2727        info: &DispatchInfoOf<Self::Call>,2728        len: usize,2729    ) -> Result<Self::Pre, TransactionValidityError> {2730        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2731        Ok((self.0, who.clone(), imbalance))2732    }27332734    fn post_dispatch(2735        pre: Self::Pre,2736        info: &DispatchInfoOf<Self::Call>,2737        post_info: &PostDispatchInfoOf<Self::Call>,2738        len: usize,2739        _result: &DispatchResult,2740    ) -> Result<(), TransactionValidityError> {2741		let (tip, who, imbalance) = pre;2742		let actual_fee = transaction_payment::Module::<T>::compute_actual_fee(2743			len as u32,2744			info,2745			post_info,2746			tip,2747		);2748		<T as transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2749		Ok(())2750    }2751}27522753// #endregion
after · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, IsSubType,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial, DispatchClass,29    },30    StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use sp_runtime::traits::StaticLookup;45use pallet_contracts::chain_extension::UncheckedFrom;46use transaction_payment::OnChargeTransaction;4748#[cfg(test)]49mod mock;5051#[cfg(test)]52mod tests;5354mod default_weights;5556pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;57pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;58pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;59pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6061// Structs62// #region6364pub type CollectionId = u32;65pub type TokenId = u32;66pub type DecimalPoints = u8;6768#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]69#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]70pub enum CollectionMode {71    Invalid,72    NFT,73    // decimal points74    Fungible(DecimalPoints),75    ReFungible,76}7778impl Default for CollectionMode {79    fn default() -> Self {80        Self::Invalid81    }82}8384impl Into<u8> for CollectionMode {85    fn into(self) -> u8 {86        match self {87            CollectionMode::Invalid => 0,88            CollectionMode::NFT => 1,89            CollectionMode::Fungible(_) => 2,90            CollectionMode::ReFungible => 3,91        }92    }93}9495#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]96#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]97pub enum AccessMode {98    Normal,99    WhiteList,100}101impl Default for AccessMode {102    fn default() -> Self {103        Self::Normal104    }105}106107#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]108#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]109pub enum SchemaVersion {110    ImageURL,111    Unique,112}113impl Default for SchemaVersion {114    fn default() -> Self {115        Self::ImageURL116    }117}118119#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]120#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]121pub struct Ownership<AccountId> {122    pub owner: AccountId,123    pub fraction: u128,124}125126#[derive(Encode, Decode, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub struct Collection<T: Config> {129    pub id: CollectionId,130    pub owner: T::AccountId,131    pub mode: CollectionMode,132    pub access: AccessMode,133    pub decimal_points: DecimalPoints,134    pub name: Vec<u16>,        // 64 include null escape char135    pub description: Vec<u16>, // 256 include null escape char136    pub token_prefix: Vec<u8>, // 16 include null escape char137    pub mint_mode: bool,138    pub offchain_schema: Vec<u8>,139    pub schema_version: SchemaVersion,140    pub sponsor: T::AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender141    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.142    pub limits: CollectionLimits<T::BlockNumber>, // Collection private restrictions 143    pub variable_on_chain_schema: Vec<u8>, //144    pub const_on_chain_schema: Vec<u8>, //145}146147#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]148#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]149pub struct NftItemType<AccountId> {150    pub owner: AccountId,151    pub const_data: Vec<u8>,152    pub variable_data: Vec<u8>,153}154155#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]156#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]157pub struct FungibleItemType {158    pub value: u128,159}160161#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]162#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]163pub struct ReFungibleItemType<AccountId> {164    pub owner: Vec<Ownership<AccountId>>,165    pub const_data: Vec<u8>,166    pub variable_data: Vec<u8>,167}168169// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]170// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]171// pub struct VestingItem<AccountId, Moment> {172//     pub sender: AccountId,173//     pub recipient: AccountId,174//     pub collection_id: CollectionId,175//     pub item_id: TokenId,176//     pub amount: u64,177//     pub vesting_date: Moment,178// }179180#[derive(Encode, Decode, Debug, Clone, PartialEq)]181#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]182pub struct CollectionLimits<BlockNumber: Encode + Decode> {183    pub account_token_ownership_limit: u32,184    pub sponsored_data_size: u32,185    /// None - setVariableMetadata is not sponsored186    /// Some(v) - setVariableMetadata is sponsored 187    ///           if there is v block between txs188    pub sponsored_data_rate_limit: Option<BlockNumber>,189    pub token_limit: u32,190191    // Timeouts for item types in passed blocks192    pub sponsor_transfer_timeout: u32,193    pub owner_can_transfer: bool,194    pub owner_can_destroy: bool,195}196197impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {198    fn default() -> Self {199        Self { 200            account_token_ownership_limit: 10_000_000, 201            token_limit: u32::max_value(),202            sponsored_data_size: u32::MAX, 203            sponsored_data_rate_limit: None,204            sponsor_transfer_timeout: 14400,205            owner_can_transfer: true,206            owner_can_destroy: true207        }208    }209}210211#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]212#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]213pub struct ChainLimits {214    pub collection_numbers_limit: u32,215    pub account_token_ownership_limit: u32,216    pub collections_admins_limit: u64,217    pub custom_data_limit: u32,218219    // Timeouts for item types in passed blocks220    pub nft_sponsor_transfer_timeout: u32,221    pub fungible_sponsor_transfer_timeout: u32,222    pub refungible_sponsor_transfer_timeout: u32,223224    // Schema limits225    pub offchain_schema_limit: u32,226    pub variable_on_chain_schema_limit: u32,227    pub const_on_chain_schema_limit: u32,228}229230pub trait WeightInfo {231	fn create_collection() -> Weight;232	fn destroy_collection() -> Weight;233	fn add_to_white_list() -> Weight;234	fn remove_from_white_list() -> Weight;235    fn set_public_access_mode() -> Weight;236    fn set_mint_permission() -> Weight;237    fn change_collection_owner() -> Weight;238    fn add_collection_admin() -> Weight;239    fn remove_collection_admin() -> Weight;240    fn set_collection_sponsor() -> Weight;241    fn confirm_sponsorship() -> Weight;242    fn remove_collection_sponsor() -> Weight;243    fn create_item(s: usize) -> Weight;244    fn burn_item() -> Weight;245    fn transfer() -> Weight;246    fn approve() -> Weight;247    fn transfer_from() -> Weight;248    fn set_offchain_schema() -> Weight;249    fn set_const_on_chain_schema() -> Weight;250    fn set_variable_on_chain_schema() -> Weight;251    fn set_variable_meta_data() -> Weight;252    fn enable_contract_sponsoring() -> Weight;253    fn set_schema_version() -> Weight;254    fn set_chain_limits() -> Weight;255    fn set_contract_sponsoring_rate_limit() -> Weight;256    fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;257    fn toggle_contract_white_list() -> Weight;258    fn add_to_contract_white_list() -> Weight;259    fn remove_from_contract_white_list() -> Weight;260    fn set_collection_limits() -> Weight;261}262263#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]264#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]265pub struct CreateNftData {266    pub const_data: Vec<u8>,267    pub variable_data: Vec<u8>,268}269270#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]271#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]272pub struct CreateFungibleData {273    pub value: u128,274}275276#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]277#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]278pub struct CreateReFungibleData {279    pub const_data: Vec<u8>,280    pub variable_data: Vec<u8>,281    pub pieces: u128,282}283284#[derive(Encode, Decode, Debug, Clone, PartialEq)]285#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]286pub enum CreateItemData {287    NFT(CreateNftData),288    Fungible(CreateFungibleData),289    ReFungible(CreateReFungibleData),290}291292impl CreateItemData {293    pub fn len(&self) -> usize {294        let len = match self {295            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),296            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),297            _ => 0298        };299        300        return len;301    }302}303304impl From<CreateNftData> for CreateItemData {305    fn from(item: CreateNftData) -> Self {306        CreateItemData::NFT(item)307    }308}309310impl From<CreateReFungibleData> for CreateItemData {311    fn from(item: CreateReFungibleData) -> Self {312        CreateItemData::ReFungible(item)313    }314}315316impl From<CreateFungibleData> for CreateItemData {317    fn from(item: CreateFungibleData) -> Self {318        CreateItemData::Fungible(item)319    }320}321322323decl_error! {324	/// Error for non-fungible-token module.325	pub enum Error for Module<T: Config> {326        /// Total collections bound exceeded.327        TotalCollectionsLimitExceeded,328		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.329        CollectionDecimalPointLimitExceeded, 330        /// Collection name can not be longer than 63 char.331        CollectionNameLimitExceeded, 332        /// Collection description can not be longer than 255 char.333        CollectionDescriptionLimitExceeded, 334        /// Token prefix can not be longer than 15 char.335        CollectionTokenPrefixLimitExceeded,336        /// This collection does not exist.337        CollectionNotFound,338        /// Item not exists.339        TokenNotFound,340        /// Admin not found341        AdminNotFound,342        /// Arithmetic calculation overflow.343        NumOverflow,       344        /// Account already has admin role.345        AlreadyAdmin,  346        /// You do not own this collection.347        NoPermission,348        /// This address is not set as sponsor, use setCollectionSponsor first.349        ConfirmUnsetSponsorFail,350        /// Collection is not in mint mode.351        PublicMintingNotAllowed,352        /// Sender parameter and item owner must be equal.353        MustBeTokenOwner,354        /// Item balance not enough.355        TokenValueTooLow,356        /// Size of item is too large.357        NftSizeLimitExceeded,358        /// No approve found359        ApproveNotFound,360        /// Requested value more than approved.361        TokenValueNotEnough,362        /// Only approved addresses can call this method.363        ApproveRequired,364        /// Address is not in white list.365        AddresNotInWhiteList,366        /// Number of collection admins bound exceeded.367        CollectionAdminsLimitExceeded,368        /// Owned tokens by a single address bound exceeded.369        AddressOwnershipLimitExceeded,370        /// Length of items properties must be greater than 0.371        EmptyArgument,372        /// const_data exceeded data limit.373        TokenConstDataLimitExceeded,374        /// variable_data exceeded data limit.375        TokenVariableDataLimitExceeded,376        /// Not NFT item data used to mint in NFT collection.377        NotNftDataUsedToMintNftCollectionToken,378        /// Not Fungible item data used to mint in Fungible collection.379        NotFungibleDataUsedToMintFungibleCollectionToken,380        /// Not Re Fungible item data used to mint in Re Fungible collection.381        NotReFungibleDataUsedToMintReFungibleCollectionToken,382        /// Unexpected collection type.383        UnexpectedCollectionType,384        /// Can't store metadata in fungible tokens.385        CantStoreMetadataInFungibleTokens,386        /// Collection token limit exceeded387        CollectionTokenLimitExceeded,388        /// Account token limit exceeded per collection389        AccountTokenLimitExceeded,390        /// Collection limit bounds per collection exceeded391        CollectionLimitBoundsExceeded,392        /// Tried to enable permissions which are only permitted to be disabled393        OwnerPermissionsCantBeReverted,394        /// Schema data size limit bound exceeded395        SchemaDataLimitExceeded,396        /// Maximum refungibility exceeded397        WrongRefungiblePieces398	}399}400401pub trait Config: system::Config + Sized + transaction_payment::Config + pallet_contracts::Config {402    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;403404    /// Weight information for extrinsics in this pallet.405	type WeightInfo: WeightInfo;406}407408#[cfg(feature = "runtime-benchmarks")]409mod benchmarking;410411// #endregion412413decl_storage! {414    trait Store for Module<T: Config> as Nft {415416        // Private members417        NextCollectionID: CollectionId;418        CreatedCollectionCount: u32;419        ChainVersion: u64;420        ItemListIndex: map hasher(identity) CollectionId => TokenId;421422        // Chain limits struct423        pub ChainLimit get(fn chain_limit) config(): ChainLimits;424425        // Bound counters426        CollectionCount: u32;427        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;428429        // Basic collections430        pub CollectionById get(fn collection_id) config(): map hasher(identity) CollectionId => Option<Collection<T>> = None;431        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;432        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;433434        /// Balance owner per collection map435        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;436437        /// second parameter: item id + owner account id + spender account id438        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;439440        /// Item collections441        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;442        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;443        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;444445        /// Index list446        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;447448        /// Tokens transfer baskets449        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;450        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;451        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;452        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;453454        /// Variable metadata sponsoring455        pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Option<T::BlockNumber> = None;456457        // Contract Sponsorship and Ownership458        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;459        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;460        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;461        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;462        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 463        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 464    }465    add_extra_genesis {466        build(|config: &GenesisConfig<T>| {467            // Modification of storage468            for (_num, _c) in &config.collection_id {469                <Module<T>>::init_collection(_c);470            }471472            for (_num, _c, _i) in &config.nft_item_id {473                <Module<T>>::init_nft_token(*_c, _i);474            }475476            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {477                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);478            }479480            for (_num, _c, _i) in &config.refungible_item_id {481                <Module<T>>::init_refungible_token(*_c, _i);482            }483        })484    }485}486487decl_event!(488    pub enum Event<T>489    where490        AccountId = <T as system::Config>::AccountId,491    {492        /// New collection was created493        /// 494        /// # Arguments495        /// 496        /// * collection_id: Globally unique identifier of newly created collection.497        /// 498        /// * mode: [CollectionMode] converted into u8.499        /// 500        /// * account_id: Collection owner.501        Created(CollectionId, u8, AccountId),502503        /// New item was created.504        /// 505        /// # Arguments506        /// 507        /// * collection_id: Id of the collection where item was created.508        /// 509        /// * item_id: Id of an item. Unique within the collection.510        ///511        /// * recipient: Owner of newly created item 512        ItemCreated(CollectionId, TokenId, AccountId),513514        /// Collection item was burned.515        /// 516        /// # Arguments517        /// 518        /// collection_id.519        /// 520        /// item_id: Identifier of burned NFT.521        ItemDestroyed(CollectionId, TokenId),522523        /// Item was transferred524        ///525        /// * collection_id: Id of collection to which item is belong526        ///527        /// * item_id: Id of an item528        ///529        /// * sender: Original owner of item530        ///531        /// * recipient: New owner of item532        ///533        /// * amount: Always 1 for NFT534        Transfer(CollectionId, TokenId, AccountId, AccountId, u128),535    }536);537538decl_module! {539    pub struct Module<T: Config> for enum Call 540    where 541        origin: T::Origin542    {543        fn deposit_event() = default;544        type Error = Error<T>;545546        fn on_initialize(now: T::BlockNumber) -> Weight {547548            if ChainVersion::get() < 2549            {550                let value = NextCollectionID::get();551                CreatedCollectionCount::put(value);552                ChainVersion::put(2);553            }554555            0556        }557558        /// 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.559        /// 560        /// # Permissions561        /// 562        /// * Anyone.563        /// 564        /// # Arguments565        /// 566        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.567        /// 568        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.569        /// 570        /// * token_prefix: UTF-8 string with token prefix.571        /// 572        /// * mode: [CollectionMode] collection type and type dependent data.573        // returns collection ID574        #[weight = <T as Config>::WeightInfo::create_collection()]575        pub fn create_collection(origin,576                                 collection_name: Vec<u16>,577                                 collection_description: Vec<u16>,578                                 token_prefix: Vec<u8>,579                                 mode: CollectionMode) -> DispatchResult {580581            // Anyone can create a collection582            let who = ensure_signed(origin)?;583584            let decimal_points = match mode {585                CollectionMode::Fungible(points) => points,586                _ => 0587            };588589            let chain_limit = ChainLimit::get();590591            // bound Total number of collections592            ensure!(CollectionCount::get() < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);593594            // check params595            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);596            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);597            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);598            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);599600            // Generate next collection ID601            let next_id = CreatedCollectionCount::get()602                .checked_add(1)603                .ok_or(Error::<T>::NumOverflow)?;604605            // bound counter606            let total = CollectionCount::get()607                .checked_add(1)608                .ok_or(Error::<T>::NumOverflow)?;609610            CreatedCollectionCount::put(next_id);611            CollectionCount::put(total);612613            let limits = CollectionLimits {614                sponsored_data_size: chain_limit.custom_data_limit,615                ..Default::default()616            };617618            // Create new collection619            let new_collection = Collection {620                id: next_id,621                owner: who.clone(),622                name: collection_name,623                mode: mode.clone(),624                mint_mode: false,625                access: AccessMode::Normal,626                description: collection_description,627                decimal_points: decimal_points,628                token_prefix: token_prefix,629                offchain_schema: Vec::new(),630                schema_version: SchemaVersion::ImageURL,631                sponsor: T::AccountId::default(),632                sponsor_confirmed: false,633                variable_on_chain_schema: Vec::new(),634                const_on_chain_schema: Vec::new(),635                limits,636            };637638            // Add new collection to map639            <CollectionById<T>>::insert(next_id, new_collection);640641            // call event642            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));643644            Ok(())645        }646647        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.648        /// 649        /// # Permissions650        /// 651        /// * Collection Owner.652        /// 653        /// # Arguments654        /// 655        /// * collection_id: collection to destroy.656        #[weight = <T as Config>::WeightInfo::destroy_collection()]657        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {658659            let sender = ensure_signed(origin)?;660            let collection = Self::get_collection(collection_id)?;661            Self::check_owner_permissions(&collection, sender)?;662            if !collection.limits.owner_can_destroy {663                fail!(Error::<T>::NoPermission);664            }665666            <AddressTokens<T>>::remove_prefix(collection_id);667            <Allowances<T>>::remove_prefix(collection_id);668            <Balance<T>>::remove_prefix(collection_id);669            <ItemListIndex>::remove(collection_id);670            <AdminList<T>>::remove(collection_id);671            <CollectionById<T>>::remove(collection_id);672            <WhiteList<T>>::remove_prefix(collection_id);673674            <NftItemList<T>>::remove_prefix(collection_id);675            <FungibleItemList<T>>::remove_prefix(collection_id);676            <ReFungibleItemList<T>>::remove_prefix(collection_id);677678            <NftTransferBasket<T>>::remove_prefix(collection_id);679            <FungibleTransferBasket<T>>::remove_prefix(collection_id);680            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);681682            <VariableMetaDataBasket<T>>::remove_prefix(collection_id);683684            if CollectionCount::get() > 0685            {686                // bound couter687                let total = CollectionCount::get()688                    .checked_sub(1)689                    .ok_or(Error::<T>::NumOverflow)?;690691                CollectionCount::put(total);692            }693694            Ok(())695        }696697        /// Add an address to white list.698        /// 699        /// # Permissions700        /// 701        /// * Collection Owner702        /// * Collection Admin703        /// 704        /// # Arguments705        /// 706        /// * collection_id.707        /// 708        /// * address.709        #[weight = <T as Config>::WeightInfo::add_to_white_list()]710        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{711712            let sender = ensure_signed(origin)?;713            let collection = Self::get_collection(collection_id)?;714            Self::check_owner_or_admin_permissions(&collection, sender)?;715716            <WhiteList<T>>::insert(collection_id, address, true);717            718            Ok(())719        }720721        /// Remove an address from white list.722        /// 723        /// # Permissions724        /// 725        /// * Collection Owner726        /// * Collection Admin727        /// 728        /// # Arguments729        /// 730        /// * collection_id.731        /// 732        /// * address.733        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]734        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{735736            let sender = ensure_signed(origin)?;737            let collection = Self::get_collection(collection_id)?;738            Self::check_owner_or_admin_permissions(&collection, sender)?;739740            <WhiteList<T>>::remove(collection_id, address);741742            Ok(())743        }744745        /// Toggle between normal and white list access for the methods with access for `Anyone`.746        /// 747        /// # Permissions748        /// 749        /// * Collection Owner.750        /// 751        /// # Arguments752        /// 753        /// * collection_id.754        /// 755        /// * mode: [AccessMode]756        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]757        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult758        {759            let sender = ensure_signed(origin)?;760761            let mut target_collection = Self::get_collection(collection_id)?;762            Self::check_owner_permissions(&target_collection, sender)?;763            target_collection.access = mode;764            Self::save_collection(target_collection);765766            Ok(())767        }768769        /// Allows Anyone to create tokens if:770        /// * White List is enabled, and771        /// * Address is added to white list, and772        /// * This method was called with True parameter773        /// 774        /// # Permissions775        /// * Collection Owner776        ///777        /// # Arguments778        /// 779        /// * collection_id.780        /// 781        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.782        #[weight = <T as Config>::WeightInfo::set_mint_permission()]783        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult784        {785            let sender = ensure_signed(origin)?;786787            let mut target_collection = Self::get_collection(collection_id)?;788            Self::check_owner_permissions(&target_collection, sender)?;789            target_collection.mint_mode = mint_permission;790            Self::save_collection(target_collection);791792            Ok(())793        }794795        /// Change the owner of the collection.796        /// 797        /// # Permissions798        /// 799        /// * Collection Owner.800        /// 801        /// # Arguments802        /// 803        /// * collection_id.804        /// 805        /// * new_owner.806        #[weight = <T as Config>::WeightInfo::change_collection_owner()]807        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {808809            let sender = ensure_signed(origin)?;810            let mut target_collection = Self::get_collection(collection_id)?;811            Self::check_owner_permissions(&target_collection, sender)?;812            target_collection.owner = new_owner;813            Self::save_collection(target_collection);814815            Ok(())816        }817818        /// Adds an admin of the Collection.819        /// 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. 820        /// 821        /// # Permissions822        /// 823        /// * Collection Owner.824        /// * Collection Admin.825        /// 826        /// # Arguments827        /// 828        /// * collection_id: ID of the Collection to add admin for.829        /// 830        /// * new_admin_id: Address of new admin to add.831        #[weight = <T as Config>::WeightInfo::add_collection_admin()]832        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {833834            let sender = ensure_signed(origin)?;835            let collection = Self::get_collection(collection_id)?;836            Self::check_owner_or_admin_permissions(&collection, sender)?;837            let mut admin_arr: Vec<T::AccountId> = Vec::new();838839            if <AdminList<T>>::contains_key(collection_id)840            {841                admin_arr = <AdminList<T>>::get(collection_id);842                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);843            }844845            // Number of collection admins846            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);847848            admin_arr.push(new_admin_id);849            <AdminList<T>>::insert(collection_id, admin_arr);850851            Ok(())852        }853854        /// 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.855        ///856        /// # Permissions857        /// 858        /// * Collection Owner.859        /// * Collection Admin.860        /// 861        /// # Arguments862        /// 863        /// * collection_id: ID of the Collection to remove admin for.864        /// 865        /// * account_id: Address of admin to remove.866        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]867        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {868869            let sender = ensure_signed(origin)?;870            let collection = Self::get_collection(collection_id)?;871            Self::check_owner_or_admin_permissions(&collection, sender)?;872            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);873874            let mut admin_arr = <AdminList<T>>::get(collection_id);875            admin_arr.retain(|i| *i != account_id);876            <AdminList<T>>::insert(collection_id, admin_arr);877878            Ok(())879        }880881        /// # Permissions882        /// 883        /// * Collection Owner884        /// 885        /// # Arguments886        /// 887        /// * collection_id.888        /// 889        /// * new_sponsor.890        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]891        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {892893            let sender = ensure_signed(origin)?;894            let mut target_collection = Self::get_collection(collection_id)?;895            Self::check_owner_permissions(&target_collection, sender)?;896897            target_collection.sponsor = new_sponsor;898            target_collection.sponsor_confirmed = false;899            Self::save_collection(target_collection);900901            Ok(())902        }903904        /// # Permissions905        /// 906        /// * Sponsor.907        /// 908        /// # Arguments909        /// 910        /// * collection_id.911        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]912        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {913914            let sender = ensure_signed(origin)?;915916            let mut target_collection = Self::get_collection(collection_id)?;917            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);918919            target_collection.sponsor_confirmed = true;920            Self::save_collection(target_collection);921922            Ok(())923        }924925        /// Switch back to pay-per-own-transaction model.926        ///927        /// # Permissions928        ///929        /// * Collection owner.930        /// 931        /// # Arguments932        /// 933        /// * collection_id.934        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]935        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {936937            let sender = ensure_signed(origin)?;938939            let mut target_collection = Self::get_collection(collection_id)?;940            Self::check_owner_permissions(&target_collection, sender)?;941942            target_collection.sponsor = T::AccountId::default();943            target_collection.sponsor_confirmed = false;944            Self::save_collection(target_collection);945946            Ok(())947        }948949        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.950        /// 951        /// # Permissions952        /// 953        /// * Collection Owner.954        /// * Collection Admin.955        /// * Anyone if956        ///     * White List is enabled, and957        ///     * Address is added to white list, and958        ///     * MintPermission is enabled (see SetMintPermission method)959        /// 960        /// # Arguments961        /// 962        /// * collection_id: ID of the collection.963        /// 964        /// * owner: Address, initial owner of the NFT.965        ///966        /// * data: Token data to store on chain.967        // #[weight =968        // (130_000_000 as Weight)969        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))970        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))971        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]972973        #[weight = <T as Config>::WeightInfo::create_item(data.len())]974        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {975976            let sender = ensure_signed(origin)?;977978            let target_collection = Self::get_collection(collection_id)?;979980            Self::can_create_items_in_collection(&target_collection, &sender, &owner)?;981            Self::validate_create_item_args(&target_collection, &data)?;982            Self::create_item_no_validation(&target_collection, owner, data)?;983984            Ok(())985        }986987        /// This method creates multiple instances of NFT Collection created with CreateCollection method.988        /// 989        /// # Permissions990        /// 991        /// * Collection Owner.992        /// * Collection Admin.993        /// * Anyone if994        ///     * White List is enabled, and995        ///     * Address is added to white list, and996        ///     * MintPermission is enabled (see SetMintPermission method)997        /// 998        /// # Arguments999        /// 1000        /// * collection_id: ID of the collection.1001        /// 1002        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].1003        /// 1004        /// * owner: Address, initial owner of the NFT.1005        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1006                               .map(|data| { data.len() })1007                               .sum())]1008        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {10091010            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1011            let sender = ensure_signed(origin)?;10121013            let target_collection = Self::get_collection(collection_id)?;10141015            Self::can_create_items_in_collection(&target_collection, &sender, &owner)?;10161017            for data in &items_data {1018                Self::validate_create_item_args(&target_collection, data)?;1019            }1020            for data in &items_data {1021                Self::create_item_no_validation(&target_collection, owner.clone(), data.clone())?;1022            }10231024            Ok(())1025        }10261027        /// Destroys a concrete instance of NFT.1028        /// 1029        /// # Permissions1030        /// 1031        /// * Collection Owner.1032        /// * Collection Admin.1033        /// * Current NFT Owner.1034        /// 1035        /// # Arguments1036        /// 1037        /// * collection_id: ID of the collection.1038        /// 1039        /// * item_id: ID of NFT to burn.1040        #[weight = <T as Config>::WeightInfo::burn_item()]1041        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10421043            let sender = ensure_signed(origin)?;10441045            // Transfer permissions check1046            let target_collection = Self::get_collection(collection_id)?;1047            ensure!(1048                Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1049                (1050                    target_collection.limits.owner_can_transfer &&1051                    Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1052                ),1053                Error::<T>::NoPermission1054            );10551056            if target_collection.access == AccessMode::WhiteList {1057                Self::check_white_list(&target_collection, &sender)?;1058            }10591060            match target_collection.mode1061            {1062                CollectionMode::NFT => Self::burn_nft_item(&target_collection, item_id)?,1063                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, &target_collection, value)?,1064                CollectionMode::ReFungible  => Self::burn_refungible_item(&target_collection, item_id, &sender)?,1065                _ => ()1066            };10671068            // call event1069            Self::deposit_event(RawEvent::ItemDestroyed(target_collection.id, item_id));10701071            Ok(())1072        }10731074        /// Change ownership of the token.1075        /// 1076        /// # Permissions1077        /// 1078        /// * Collection Owner1079        /// * Collection Admin1080        /// * Current NFT owner1081        ///1082        /// # Arguments1083        /// 1084        /// * recipient: Address of token recipient.1085        /// 1086        /// * collection_id.1087        /// 1088        /// * item_id: ID of the item1089        ///     * Non-Fungible Mode: Required.1090        ///     * Fungible Mode: Ignored.1091        ///     * Re-Fungible Mode: Required.1092        /// 1093        /// * value: Amount to transfer.1094        ///     * Non-Fungible Mode: Ignored1095        ///     * Fungible Mode: Must specify transferred amount1096        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1097        #[weight = <T as Config>::WeightInfo::transfer()]1098        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1099            let sender = ensure_signed(origin)?;1100            let collection = Self::get_collection(collection_id)?;11011102            Self::transfer_internal(sender, recipient, &collection, item_id, value)1103        }11041105        /// Set, change, or remove approved address to transfer the ownership of the NFT.1106        /// 1107        /// # Permissions1108        /// 1109        /// * Collection Owner1110        /// * Collection Admin1111        /// * Current NFT owner1112        /// 1113        /// # Arguments1114        /// 1115        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1116        /// 1117        /// * collection_id.1118        /// 1119        /// * item_id: ID of the item.1120        #[weight = <T as Config>::WeightInfo::approve()]1121        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {11221123            let sender = ensure_signed(origin)?;1124            let target_collection = Self::get_collection(collection_id)?;11251126            Self::token_exists(&target_collection, item_id, &sender)?;11271128            // Transfer permissions check1129            let allowance_limit = if (1130                target_collection.limits.owner_can_transfer &&1131                Self::is_owner_or_admin_permissions(1132                    &target_collection,1133                    sender.clone(),1134                )1135            ) {1136                None1137            } else if let Some(amount) = Self::owned_amount(1138                sender.clone(),1139                &target_collection,1140                item_id,1141            ) {1142                Some(amount)1143            } else {1144                fail!(Error::<T>::NoPermission);1145            };11461147            if target_collection.access == AccessMode::WhiteList {1148                Self::check_white_list(&target_collection, &sender)?;1149                Self::check_white_list(&target_collection, &spender)?;1150            }11511152            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1153            let mut allowance: u128 = amount;1154            if allowance_exists {1155                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1156            }1157            if let Some(limit) = allowance_limit {1158                ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1159            }1160            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11611162            Ok(())1163        }1164        1165        /// 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.1166        /// 1167        /// # Permissions1168        /// * Collection Owner1169        /// * Collection Admin1170        /// * Current NFT owner1171        /// * Address approved by current NFT owner1172        /// 1173        /// # Arguments1174        /// 1175        /// * from: Address that owns token.1176        /// 1177        /// * recipient: Address of token recipient.1178        /// 1179        /// * collection_id.1180        /// 1181        /// * item_id: ID of the item.1182        /// 1183        /// * value: Amount to transfer.1184        #[weight = <T as Config>::WeightInfo::transfer_from()]1185        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11861187            let sender = ensure_signed(origin)?;1188            let target_collection = Self::get_collection(collection_id)?;11891190            let mut appoved_transfer = false;11911192            // Check approval1193            let mut approval: u128 = 0;1194            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1195                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1196                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1197                appoved_transfer = true;1198            }11991200            // Limits check1201            Self::is_correct_transfer(&target_collection, &recipient)?;12021203            // Transfer permissions check         1204            ensure!(1205                appoved_transfer || 1206                (1207                    target_collection.limits.owner_can_transfer &&1208                    Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1209                ),1210                Error::<T>::NoPermission1211            );12121213            if target_collection.access == AccessMode::WhiteList {1214                Self::check_white_list(&target_collection, &sender)?;1215                Self::check_white_list(&target_collection, &recipient)?;1216            }12171218            // Reduce approval by transferred amount or remove if remaining approval drops to 01219            if approval.checked_sub(value).unwrap_or(0) > 0 {1220                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1221            }1222            else {1223                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1224            }12251226            match target_collection.mode1227            {1228                CollectionMode::NFT => Self::transfer_nft(&target_collection, item_id, from, recipient)?,1229                CollectionMode::Fungible(_)  => Self::transfer_fungible(&target_collection, value, &from, &recipient)?,1230                CollectionMode::ReFungible  => Self::transfer_refungible(&target_collection, item_id, value, from.clone(), recipient)?,1231                _ => ()1232            };12331234            Ok(())1235        }12361237        // #[weight = 0]1238        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {12391240        //     // let no_perm_mes = "You do not have permissions to modify this collection";1241        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1242        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1243        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);12441245        //     // // on_nft_received  call12461247        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;12481249        //     Ok(())1250        // }12511252        /// Set off-chain data schema.1253        /// 1254        /// # Permissions1255        /// 1256        /// * Collection Owner1257        /// * Collection Admin1258        /// 1259        /// # Arguments1260        /// 1261        /// * collection_id.1262        /// 1263        /// * schema: String representing the offchain data schema.1264        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1265        pub fn set_variable_meta_data (1266            origin,1267            collection_id: CollectionId,1268            item_id: TokenId,1269            data: Vec<u8>1270        ) -> DispatchResult {1271            let sender = ensure_signed(origin)?;1272            1273            let target_collection = Self::get_collection(collection_id)?;1274            Self::token_exists(&target_collection, item_id, &sender)?;12751276            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12771278            // Modify permissions check1279            ensure!(Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1280                Self::is_owner_or_admin_permissions(&target_collection, sender.clone()),1281                Error::<T>::NoPermission);12821283            match target_collection.mode1284            {1285                CollectionMode::NFT => Self::set_nft_variable_data(&target_collection, item_id, data)?,1286                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(&target_collection, item_id, data)?,1287                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1288                _ => fail!(Error::<T>::UnexpectedCollectionType)1289            };12901291            Ok(())1292        }1293 1294        /// Set schema standard1295        /// ImageURL1296        /// Unique1297        /// 1298        /// # Permissions1299        /// 1300        /// * Collection Owner1301        /// * Collection Admin1302        /// 1303        /// # Arguments1304        /// 1305        /// * collection_id.1306        /// 1307        /// * schema: SchemaVersion: enum1308        #[weight = <T as Config>::WeightInfo::set_schema_version()]1309        pub fn set_schema_version(1310            origin,1311            collection_id: CollectionId,1312            version: SchemaVersion1313        ) -> DispatchResult {1314            let sender = ensure_signed(origin)?;1315            let mut target_collection = Self::get_collection(collection_id)?;1316            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;1317            target_collection.schema_version = version;1318            Self::save_collection(target_collection);13191320            Ok(())1321        }13221323        /// Set off-chain data schema.1324        /// 1325        /// # Permissions1326        /// 1327        /// * Collection Owner1328        /// * Collection Admin1329        /// 1330        /// # Arguments1331        /// 1332        /// * collection_id.1333        /// 1334        /// * schema: String representing the offchain data schema.1335        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1336        pub fn set_offchain_schema(1337            origin,1338            collection_id: CollectionId,1339            schema: Vec<u8>1340        ) -> DispatchResult {1341            let sender = ensure_signed(origin)?;1342            let mut target_collection = Self::get_collection(collection_id)?;1343            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;13441345            // check schema limit1346            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");13471348            target_collection.offchain_schema = schema;1349            Self::save_collection(target_collection);13501351            Ok(())1352        }13531354        /// Set const on-chain data schema.1355        /// 1356        /// # Permissions1357        /// 1358        /// * Collection Owner1359        /// * Collection Admin1360        /// 1361        /// # Arguments1362        /// 1363        /// * collection_id.1364        /// 1365        /// * schema: String representing the const on-chain data schema.1366        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1367        pub fn set_const_on_chain_schema (1368            origin,1369            collection_id: CollectionId,1370            schema: Vec<u8>1371        ) -> DispatchResult {1372            let sender = ensure_signed(origin)?;1373            let mut target_collection = Self::get_collection(collection_id)?;1374            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;13751376            // check schema limit1377            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");13781379            target_collection.const_on_chain_schema = schema;1380            Self::save_collection(target_collection);13811382            Ok(())1383        }13841385        /// Set variable on-chain data schema.1386        /// 1387        /// # Permissions1388        /// 1389        /// * Collection Owner1390        /// * Collection Admin1391        /// 1392        /// # Arguments1393        /// 1394        /// * collection_id.1395        /// 1396        /// * schema: String representing the variable on-chain data schema.1397        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1398        pub fn set_variable_on_chain_schema (1399            origin,1400            collection_id: CollectionId,1401            schema: Vec<u8>1402        ) -> DispatchResult {1403            let sender = ensure_signed(origin)?;1404            let mut target_collection = Self::get_collection(collection_id)?;1405            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;14061407            // check schema limit1408            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14091410            target_collection.variable_on_chain_schema = schema;1411            Self::save_collection(target_collection);14121413            Ok(())1414        }14151416        // Sudo permissions function1417        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1418        pub fn set_chain_limits(1419            origin,1420            limits: ChainLimits1421        ) -> DispatchResult {14221423            #[cfg(not(feature = "runtime-benchmarks"))]1424            ensure_root(origin)?;14251426            <ChainLimit>::put(limits);1427            Ok(())1428        }14291430        /// Enable smart contract self-sponsoring.1431        /// 1432        /// # Permissions1433        /// 1434        /// * Contract Owner1435        /// 1436        /// # Arguments1437        /// 1438        /// * contract address1439        /// * enable flag1440        /// 1441        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1442        pub fn enable_contract_sponsoring(1443            origin,1444            contract_address: T::AccountId,1445            enable: bool1446        ) -> DispatchResult {14471448            let sender = ensure_signed(origin)?;14491450            #[cfg(feature = "runtime-benchmarks")]1451            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14521453            Self::ensure_contract_owned(sender, &contract_address)?;14541455            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1456            Ok(())1457        }14581459        /// Set the rate limit for contract sponsoring to specified number of blocks.1460        /// 1461        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1462        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1463        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1464        /// from contract endowment if there are at least B blocks between such transactions. 1465        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1466        /// 1467        /// # Permissions1468        /// 1469        /// * Contract Owner1470        /// 1471        /// # Arguments1472        /// 1473        /// -`contract_address`: Address of the contract to sponsor1474        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1475        /// 1476        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1477        pub fn set_contract_sponsoring_rate_limit(1478            origin,1479            contract_address: T::AccountId,1480            rate_limit: T::BlockNumber1481        ) -> DispatchResult {1482            let sender = ensure_signed(origin)?;14831484            #[cfg(feature = "runtime-benchmarks")]1485            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14861487            Self::ensure_contract_owned(sender, &contract_address)?;1488            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1489            Ok(())1490        }14911492        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1493        /// 1494        /// # Permissions1495        /// 1496        /// * Address that deployed smart contract.1497        /// 1498        /// # Arguments1499        /// 1500        /// -`contract_address`: Address of the contract.1501        /// 1502        /// - `enable`: .  1503        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1504        pub fn toggle_contract_white_list(1505            origin,1506            contract_address: T::AccountId,1507            enable: bool1508        ) -> DispatchResult {1509            let sender = ensure_signed(origin)?;15101511            #[cfg(feature = "runtime-benchmarks")]1512            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15131514            Self::ensure_contract_owned(sender, &contract_address)?;1515            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1516            Ok(())1517        }1518        1519        /// Add an address to smart contract white list.1520        /// 1521        /// # Permissions1522        /// 1523        /// * Address that deployed smart contract.1524        /// 1525        /// # Arguments1526        /// 1527        /// -`contract_address`: Address of the contract.1528        ///1529        /// -`account_address`: Address to add.1530        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1531        pub fn add_to_contract_white_list(1532            origin,1533            contract_address: T::AccountId,1534            account_address: T::AccountId1535        ) -> DispatchResult {1536            let sender = ensure_signed(origin)?;15371538            #[cfg(feature = "runtime-benchmarks")]1539            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1540            1541            Self::ensure_contract_owned(sender, &contract_address)?;      1542            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1543            Ok(())1544        }15451546        /// Remove an address from smart contract white list.1547        /// 1548        /// # Permissions1549        /// 1550        /// * Address that deployed smart contract.1551        /// 1552        /// # Arguments1553        /// 1554        /// -`contract_address`: Address of the contract.1555        ///1556        /// -`account_address`: Address to remove.1557        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1558        pub fn remove_from_contract_white_list(1559            origin,1560            contract_address: T::AccountId,1561            account_address: T::AccountId1562        ) -> DispatchResult {1563            let sender = ensure_signed(origin)?;15641565            #[cfg(feature = "runtime-benchmarks")]1566            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15671568            Self::ensure_contract_owned(sender, &contract_address)?;1569            <ContractWhiteList<T>>::remove(contract_address, account_address);1570            Ok(())1571        }15721573        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1574        pub fn set_collection_limits(1575            origin,1576            collection_id: u32,1577            new_limits: CollectionLimits<T::BlockNumber>,1578        ) -> DispatchResult {1579            let sender = ensure_signed(origin)?;1580            let mut target_collection = Self::get_collection(collection_id)?;1581            Self::check_owner_permissions(&target_collection, sender.clone())?;1582            let old_limits = target_collection.limits;1583            let chain_limits = ChainLimit::get();15841585            // collection bounds1586            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1587                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1588                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1589                Error::<T>::CollectionLimitBoundsExceeded);15901591            // token_limit   check  prev1592            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1593            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);15941595            ensure!(1596                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1597                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1598                Error::<T>::OwnerPermissionsCantBeReverted,1599            );16001601            target_collection.limits = new_limits;1602            Self::save_collection(target_collection);16031604            Ok(())1605        } 1606    }1607}16081609impl<T: Config> Module<T> {16101611    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, target_collection: &Collection<T>, item_id: TokenId, value: u128) -> DispatchResult {1612        // Limits check1613        Self::is_correct_transfer(target_collection, &recipient)?;16141615        // Transfer permissions check1616        ensure!(Self::is_item_owner(sender.clone(), target_collection, item_id) ||1617            Self::is_owner_or_admin_permissions(target_collection, sender.clone()),1618            Error::<T>::NoPermission);16191620        if target_collection.access == AccessMode::WhiteList {1621            Self::check_white_list(target_collection, &sender)?;1622            Self::check_white_list(target_collection, &recipient)?;1623        }16241625        match target_collection.mode1626        {1627            CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1628            CollectionMode::Fungible(_)  => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1629            CollectionMode::ReFungible  => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1630            _ => ()1631        };16321633        Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender, recipient, value));16341635        Ok(())1636    }163716381639    fn is_correct_transfer(collection: &Collection<T>, recipient: &T::AccountId) -> DispatchResult {1640        let collection_id = collection.id;16411642        // check token limit and account token limit1643        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1644        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1645        1646        Ok(())1647    }16481649    fn can_create_items_in_collection(collection: &Collection<T>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {1650        let collection_id = collection.id;16511652        // check token limit and account token limit1653        let total_items: u32 = ItemListIndex::get(collection_id);1654        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1655        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1656        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);16571658        if !Self::is_owner_or_admin_permissions(collection, sender.clone()) {1659            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1660            Self::check_white_list(collection, owner)?;1661            Self::check_white_list(collection, sender)?;1662        }16631664        Ok(())1665    }16661667    fn validate_create_item_args(target_collection: &Collection<T>, data: &CreateItemData) -> DispatchResult {1668        match target_collection.mode1669        {1670            CollectionMode::NFT => {1671                if let CreateItemData::NFT(data) = data {1672                    // check sizes1673                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1674                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1675                } else {1676                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1677                }1678            },1679            CollectionMode::Fungible(_) => {1680                if let CreateItemData::Fungible(_) = data {1681                } else {1682                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1683                }1684            },1685            CollectionMode::ReFungible => {1686                if let CreateItemData::ReFungible(data) = data {16871688                    // check sizes1689                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1690                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16911692                    // Check refungibility limits1693                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1694                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1695                } else {1696                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1697                }1698            },1699            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1700        };17011702        Ok(())1703    }17041705    fn create_item_no_validation(collection: &Collection<T>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1706        let collection_id = collection.id;17071708        match data1709        {1710            CreateItemData::NFT(data) => {1711                let item = NftItemType {1712                    owner: owner.clone(),1713                    const_data: data.const_data,1714                    variable_data: data.variable_data1715                };17161717                Self::add_nft_item(collection, item)?;1718            },1719            CreateItemData::Fungible(data) => {1720                Self::add_fungible_item(collection, &owner, data.value)?;1721            },1722            CreateItemData::ReFungible(data) => {1723                let mut owner_list = Vec::new();1724                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});17251726                let item = ReFungibleItemType {1727                    owner: owner_list,1728                    const_data: data.const_data,1729                    variable_data: data.variable_data1730                };17311732                Self::add_refungible_item(collection, item)?;1733            }1734        };17351736        // call event1737        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id), owner));17381739        Ok(())1740    }17411742    fn add_fungible_item(collection: &Collection<T>, owner: &T::AccountId, value: u128) -> DispatchResult {1743        let collection_id = collection.id;17441745        // Does new owner already have an account?1746        let mut balance: u128 = 0;1747        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1748            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1749        } 17501751        // Mint 1752        let item = FungibleItemType {1753            value: balance + value1754        };1755        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);17561757        // Update balance1758        let new_balance = <Balance<T>>::get(collection_id, owner)1759            .checked_add(value)1760            .ok_or(Error::<T>::NumOverflow)?;1761        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17621763        Ok(())1764    }17651766    fn add_refungible_item(collection: &Collection<T>, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1767        let collection_id = collection.id;17681769        let current_index = <ItemListIndex>::get(collection_id)1770            .checked_add(1)1771            .ok_or(Error::<T>::NumOverflow)?;1772        let itemcopy = item.clone();17731774        let value = item.owner.first().unwrap().fraction;1775        let owner = item.owner.first().unwrap().owner.clone();17761777        Self::add_token_index(collection_id, current_index, &owner)?;17781779        <ItemListIndex>::insert(collection_id, current_index);1780        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17811782        // Update balance1783        let new_balance = <Balance<T>>::get(collection_id, &owner)1784            .checked_add(value)1785            .ok_or(Error::<T>::NumOverflow)?;1786        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17871788        Ok(())1789    }17901791    fn add_nft_item(collection: &Collection<T>, item: NftItemType<T::AccountId>) -> DispatchResult {1792        let collection_id = collection.id;17931794        let current_index = <ItemListIndex>::get(collection_id)1795            .checked_add(1)1796            .ok_or(Error::<T>::NumOverflow)?;17971798        let item_owner = item.owner.clone();1799        Self::add_token_index(collection_id, current_index, &item.owner)?;18001801        <ItemListIndex>::insert(collection_id, current_index);1802        <NftItemList<T>>::insert(collection_id, current_index, item);18031804        // Update balance1805        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1806            .checked_add(1)1807            .ok_or(Error::<T>::NumOverflow)?;1808        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);18091810        Ok(())1811    }18121813    fn burn_refungible_item(1814        collection: &Collection<T>,1815        item_id: TokenId,1816        owner: &T::AccountId,1817    ) -> DispatchResult {1818        let collection_id = collection.id;18191820        ensure!(1821            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1822            Error::<T>::TokenNotFound1823        );1824        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1825        let rft_balance = token1826            .owner1827            .iter()1828            .filter(|&i| i.owner == *owner)1829            .next()1830            .unwrap();1831        Self::remove_token_index(collection_id, item_id, owner)?;18321833        // update balance1834        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1835            .checked_sub(rft_balance.fraction)1836            .ok_or(Error::<T>::NumOverflow)?;1837        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);18381839        // Re-create owners list with sender removed1840        let index = token1841            .owner1842            .iter()1843            .position(|i| i.owner == *owner)1844            .unwrap();1845        token.owner.remove(index);1846        let owner_count = token.owner.len();18471848        // Burn the token completely if this was the last (only) owner1849        if owner_count == 0 {1850            <ReFungibleItemList<T>>::remove(collection_id, item_id);1851            <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1852        }1853        else {1854            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1855        }18561857        Ok(())1858    }18591860    fn burn_nft_item(collection: &Collection<T>, item_id: TokenId) -> DispatchResult {1861        let collection_id = collection.id;18621863        ensure!(1864            <NftItemList<T>>::contains_key(collection_id, item_id),1865            Error::<T>::TokenNotFound1866        );1867        let item = <NftItemList<T>>::get(collection_id, item_id);1868        Self::remove_token_index(collection_id, item_id, &item.owner)?;18691870        // update balance1871        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1872            .checked_sub(1)1873            .ok_or(Error::<T>::NumOverflow)?;1874        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1875        <NftItemList<T>>::remove(collection_id, item_id);1876        <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18771878        Ok(())1879    }18801881    fn burn_fungible_item(owner: &T::AccountId, collection: &Collection<T>, value: u128) -> DispatchResult {1882        let collection_id = collection.id;18831884        ensure!(1885            <FungibleItemList<T>>::contains_key(collection_id, owner),1886            Error::<T>::TokenNotFound1887        );1888        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1889        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18901891        // update balance1892        let new_balance = <Balance<T>>::get(collection_id, owner)1893            .checked_sub(value)1894            .ok_or(Error::<T>::NumOverflow)?;1895        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18961897        if balance.value - value > 0 {1898            balance.value -= value;1899            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1900        }1901        else {1902            <FungibleItemList<T>>::remove(collection_id, owner);1903        }19041905        Ok(())1906    }19071908    pub fn get_collection(collection_id: CollectionId) -> Result<Collection<T>, sp_runtime::DispatchError> {1909        Ok(<CollectionById<T>>::get(collection_id)1910            .ok_or(Error::<T>::CollectionNotFound)?)1911    }19121913    fn save_collection(collection: Collection<T>) {1914        <CollectionById<T>>::insert(collection.id, collection);1915    }19161917    fn check_owner_permissions(target_collection: &Collection<T>, subject: T::AccountId) -> DispatchResult {1918        ensure!(1919            subject == target_collection.owner,1920            Error::<T>::NoPermission1921        );19221923        Ok(())1924    }19251926    fn is_owner_or_admin_permissions(collection: &Collection<T>, subject: T::AccountId) -> bool {1927        let mut result: bool = subject == collection.owner;1928        let exists = <AdminList<T>>::contains_key(collection.id);19291930        if !result & exists {1931            if <AdminList<T>>::get(collection.id).contains(&subject) {1932                result = true1933            }1934        }19351936        result1937    }19381939    fn check_owner_or_admin_permissions(1940        collection: &Collection<T>,1941        subject: T::AccountId,1942    ) -> DispatchResult {1943        let result = Self::is_owner_or_admin_permissions(collection, subject.clone());19441945        ensure!(1946            result,1947            Error::<T>::NoPermission1948        );1949        Ok(())1950    }19511952    fn owned_amount(1953        subject: T::AccountId,1954        target_collection: &Collection<T>,1955        item_id: TokenId,1956    ) -> Option<u128> {1957        let collection_id = target_collection.id;19581959        match target_collection.mode {1960            CollectionMode::NFT => {1961                if <NftItemList<T>>::get(collection_id, item_id).owner == subject {1962                    return Some(1)1963                }1964                None1965            },1966            CollectionMode::Fungible(_) => {1967                if <FungibleItemList<T>>::contains_key(collection_id, &subject) {1968                    return Some(<FungibleItemList<T>>::get(collection_id, &subject)1969                        .value);1970                }1971                None1972            },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::AccountId, target_collection: &Collection<T>, item_id: TokenId) -> bool {1983        let collection_id = target_collection.id;19841985        match target_collection.mode {1986            CollectionMode::NFT => {1987                <NftItemList<T>>::get(collection_id, item_id).owner == subject1988            }1989            CollectionMode::Fungible(_) => {1990                <FungibleItemList<T>>::contains_key(collection_id, &subject)1991            }1992            CollectionMode::ReFungible => {1993                <ReFungibleItemList<T>>::get(collection_id, item_id)1994                    .owner1995                    .iter()1996                    .any(|i| i.owner == subject)1997            }1998            CollectionMode::Invalid => false,1999        }2000    }20012002    fn check_white_list(collection: &Collection<T>, address: &T::AccountId) -> DispatchResult {2003        let collection_id = collection.id;20042005        let mes = Error::<T>::AddresNotInWhiteList;2006        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);20072008        Ok(())2009    }20102011    /// Check if token exists. In case of Fungible, check if there is an entry for 2012    /// the owner in fungible balances double map2013    fn token_exists(2014        target_collection: &Collection<T>,2015        item_id: TokenId,2016        owner: &T::AccountId2017    ) -> DispatchResult {2018        let collection_id = target_collection.id;2019        let exists = match target_collection.mode2020        {2021            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2022            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),2023            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2024            _ => false2025        };20262027        ensure!(exists == true, Error::<T>::TokenNotFound);2028        Ok(())2029    }20302031    fn transfer_fungible(2032        collection: &Collection<T>,2033        value: u128,2034        owner: &T::AccountId,2035        recipient: &T::AccountId,2036    ) -> DispatchResult {2037        let collection_id = collection.id;2038        Self::token_exists(&collection, 0, owner)?;20392040        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);2041        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);20422043        // Send balance to recipient (updates balanceOf of recipient)2044        Self::add_fungible_item(collection, recipient, value)?;20452046        // update balanceOf of sender2047        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);20482049        // Reduce or remove sender2050        if balance.value == value {2051            <FungibleItemList<T>>::remove(collection_id, owner);2052        }2053        else {2054            balance.value -= value;2055            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);2056        }20572058        Ok(())2059    }20602061    fn transfer_refungible(2062        collection: &Collection<T>,2063        item_id: TokenId,2064        value: u128,2065        owner: T::AccountId,2066        new_owner: T::AccountId,2067    ) -> DispatchResult {2068        let collection_id = collection.id;2069        Self::token_exists(collection, item_id, &owner)?;20702071        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);2072        let item = full_item2073            .owner2074            .iter()2075            .filter(|i| i.owner == owner)2076            .next()2077            .ok_or(Error::<T>::NumOverflow)?;2078        let amount = item.fraction;20792080        ensure!(amount >= value, Error::<T>::TokenValueTooLow);20812082        // update balance2083        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2084            .checked_sub(value)2085            .ok_or(Error::<T>::NumOverflow)?;2086        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20872088        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2089            .checked_add(value)2090            .ok_or(Error::<T>::NumOverflow)?;2091        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20922093        let old_owner = item.owner.clone();2094        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20952096        // transfer2097        if amount == value && !new_owner_has_account {2098            // change owner2099            // new owner do not have account2100            let mut new_full_item = full_item.clone();2101            new_full_item2102                .owner2103                .iter_mut()2104                .find(|i| i.owner == owner)2105                .unwrap()2106                .owner = new_owner.clone();2107            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);21082109            // update index collection2110            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2111        } else {2112            let mut new_full_item = full_item.clone();2113            new_full_item2114                .owner2115                .iter_mut()2116                .find(|i| i.owner == owner)2117                .unwrap()2118                .fraction -= value;21192120            // separate amount2121            if new_owner_has_account {2122                // new owner has account2123                new_full_item2124                    .owner2125                    .iter_mut()2126                    .find(|i| i.owner == new_owner)2127                    .unwrap()2128                    .fraction += value;2129            } else {2130                // new owner do not have account2131                new_full_item.owner.push(Ownership {2132                    owner: new_owner.clone(),2133                    fraction: value,2134                });2135                Self::add_token_index(collection_id, item_id, &new_owner)?;2136            }21372138            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2139        }21402141        Ok(())2142    }21432144    fn transfer_nft(2145        collection: &Collection<T>,2146        item_id: TokenId,2147        sender: T::AccountId,2148        new_owner: T::AccountId,2149    ) -> DispatchResult {2150        let collection_id = collection.id;2151        Self::token_exists(&collection, item_id, &sender)?;21522153        let mut item = <NftItemList<T>>::get(collection_id, item_id);21542155        ensure!(2156            sender == item.owner,2157            Error::<T>::MustBeTokenOwner2158        );21592160        // update balance2161        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2162            .checked_sub(1)2163            .ok_or(Error::<T>::NumOverflow)?;2164        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);21652166        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2167            .checked_add(1)2168            .ok_or(Error::<T>::NumOverflow)?;2169        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);21702171        // change owner2172        let old_owner = item.owner.clone();2173        item.owner = new_owner.clone();2174        <NftItemList<T>>::insert(collection_id, item_id, item);21752176        // update index collection2177        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21782179        Ok(())2180    }2181    2182    fn set_re_fungible_variable_data(2183        collection: &Collection<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);21892190        item.variable_data = data;21912192        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21932194        Ok(())2195    }21962197    fn set_nft_variable_data(2198        collection: &Collection<T>,2199        item_id: TokenId,2200        data: Vec<u8>2201    ) -> DispatchResult {2202        let collection_id = collection.id;2203        let mut item = <NftItemList<T>>::get(collection_id, item_id);2204        2205        item.variable_data = data;22062207        <NftItemList<T>>::insert(collection_id, item_id, item);2208        2209        Ok(())2210    }22112212    fn init_collection(item: &Collection<T>) {2213        // check params2214        assert!(2215            item.decimal_points <= MAX_DECIMAL_POINTS,2216            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2217        );2218        assert!(2219            item.name.len() <= 64,2220            "Collection name can not be longer than 63 char"2221        );2222        assert!(2223            item.name.len() <= 256,2224            "Collection description can not be longer than 255 char"2225        );2226        assert!(2227            item.token_prefix.len() <= 16,2228            "Token prefix can not be longer than 15 char"2229        );22302231        // Generate next collection ID2232        let next_id = CreatedCollectionCount::get()2233            .checked_add(1)2234            .unwrap();22352236        CreatedCollectionCount::put(next_id);2237    }22382239    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2240        let current_index = <ItemListIndex>::get(collection_id)2241            .checked_add(1)2242            .unwrap();22432244        let item_owner = item.owner.clone();2245        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22462247        <ItemListIndex>::insert(collection_id, current_index);22482249        // Update balance2250        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2251            .checked_add(1)2252            .unwrap();2253        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2254    }22552256    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2257        let current_index = <ItemListIndex>::get(collection_id)2258            .checked_add(1)2259            .unwrap();22602261        Self::add_token_index(collection_id, current_index, owner).unwrap();22622263        <ItemListIndex>::insert(collection_id, current_index);22642265        // Update balance2266        let new_balance = <Balance<T>>::get(collection_id, owner)2267            .checked_add(item.value)2268            .unwrap();2269        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2270    }22712272    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2273        let current_index = <ItemListIndex>::get(collection_id)2274            .checked_add(1)2275            .unwrap();22762277        let value = item.owner.first().unwrap().fraction;2278        let owner = item.owner.first().unwrap().owner.clone();22792280        Self::add_token_index(collection_id, current_index, &owner).unwrap();22812282        <ItemListIndex>::insert(collection_id, current_index);22832284        // Update balance2285        let new_balance = <Balance<T>>::get(collection_id, &owner)2286            .checked_add(value)2287            .unwrap();2288        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2289    }22902291    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {2292        // add to account limit2293        if <AccountItemCount<T>>::contains_key(owner) {22942295            // bound Owned tokens by a single address2296            let count = <AccountItemCount<T>>::get(owner);2297            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);22982299            <AccountItemCount<T>>::insert(owner.clone(), count2300                .checked_add(1)2301                .ok_or(Error::<T>::NumOverflow)?);2302        }2303        else {2304            <AccountItemCount<T>>::insert(owner.clone(), 1);2305        }23062307        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2308        if list_exists {2309            let mut list = <AddressTokens<T>>::get(collection_id, owner);2310            let item_contains = list.contains(&item_index.clone());23112312            if !item_contains {2313                list.push(item_index.clone());2314            }23152316            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2317        } else {2318            let mut itm = Vec::new();2319            itm.push(item_index.clone());2320            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2321        }23222323        Ok(())2324    }23252326    fn remove_token_index(2327        collection_id: CollectionId,2328        item_index: TokenId,2329        owner: &T::AccountId,2330    ) -> DispatchResult {23312332        // update counter2333        <AccountItemCount<T>>::insert(owner.clone(), 2334            <AccountItemCount<T>>::get(owner)2335            .checked_sub(1)2336            .ok_or(Error::<T>::NumOverflow)?);233723382339        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2340        if list_exists {2341            let mut list = <AddressTokens<T>>::get(collection_id, owner);2342            let item_contains = list.contains(&item_index.clone());23432344            if item_contains {2345                list.retain(|&item| item != item_index);2346                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2347            }2348        }23492350        Ok(())2351    }23522353    fn move_token_index(2354        collection_id: CollectionId,2355        item_index: TokenId,2356        old_owner: &T::AccountId,2357        new_owner: &T::AccountId,2358    ) -> DispatchResult {2359        Self::remove_token_index(collection_id, item_index, old_owner)?;2360        Self::add_token_index(collection_id, item_index, new_owner)?;23612362        Ok(())2363    }2364    2365    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2366        if <ContractOwner<T>>::contains_key(contract.clone()) {2367            let owner = <ContractOwner<T>>::get(contract);2368            ensure!(account == owner, Error::<T>::NoPermission);2369        } else {2370            fail!(Error::<T>::NoPermission);2371        }23722373        Ok(())2374    }2375}23762377////////////////////////////////////////////////////////////////////////////////////////////////////2378// Economic models2379// #region23802381/// Fee multiplier.2382pub type Multiplier = FixedU128;23832384type BalanceOf<T> = <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::Balance;23852386/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2387/// in the queue.2388#[derive(Encode, Decode, Clone, Eq, PartialEq)]2389pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);23902391impl<T: Config + Send + Sync> sp_std::fmt::Debug 2392    for ChargeTransactionPayment<T>2393{2394	#[cfg(feature = "std")]2395	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2396		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2397	}2398	#[cfg(not(feature = "std"))]2399	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2400		Ok(())2401	}2402}24032404impl<T: Config> ChargeTransactionPayment<T>2405where2406    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2407    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2408    T::AccountId: AsRef<[u8]>,2409    T::AccountId: UncheckedFrom<T::Hash>,2410{2411    fn traditional_fee(2412        len: usize,2413        info: &DispatchInfoOf<T::Call>,2414        tip: BalanceOf<T>,2415    ) -> BalanceOf<T>2416    where2417        T::Call: Dispatchable<Info = DispatchInfo>,2418    {2419        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2420    }24212422	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2423        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2424        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2425        let len_saturation = max_block_length as u64 / (len as u64).max(1);2426        let coefficient: BalanceOf<T> = weight_saturation2427            .min(len_saturation)2428            .saturated_into::<BalanceOf<T>>();2429        final_fee2430            .saturating_mul(coefficient)2431            .saturated_into::<TransactionPriority>()2432    }24332434    fn withdraw_fee(2435        &self,2436        who: &T::AccountId,2437        call: &T::Call,2438        info: &DispatchInfoOf<T::Call>,2439        len: usize,2440	) -> Result<2441		(2442			BalanceOf<T>,2443			<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2444		),2445		TransactionValidityError,2446	> {2447        let tip = self.0;24482449        // Set fee based on call type. Creating collection costs 1 Unique.2450        // All other transactions have traditional fees so far2451        // let fee = match call.is_sub_type() {2452        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2453        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2454        //                                                 // _ => <BalanceOf<T>>::from(100)2455        // };2456        let fee = Self::traditional_fee(len, info, tip);24572458        // Only mess with balances if fee is not zero.2459        if fee.is_zero() {2460            return <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2461			.map(|i| (fee, i));2462        }24632464        // Determine who is paying transaction fee based on ecnomic model2465        // Parse call to extract collection ID and access collection sponsor2466        let mut sponsor: Option<T::AccountId> = (|| match IsSubType::<Call<T>>::is_sub_type(call) {2467            Some(Call::create_item(collection_id, _owner, _properties)) => {2468                let collection = <CollectionById<T>>::get(collection_id)?;24692470                // sponsor timeout2471                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;24722473                let limit = collection.limits.sponsor_transfer_timeout;2474                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2475                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2476                    let limit_time = last_tx_block + limit.into();2477                    if block_number <= limit_time {2478                        return None;2479                    }2480                }2481                <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);24822483                // check free create limit2484                if (collection.limits.sponsored_data_size >= (_properties.len() as u32)) &&2485                   (collection.sponsor_confirmed)2486                {2487                    Some(collection.sponsor)2488                } else {2489                    None2490                }2491            }2492            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2493                let collection = <CollectionById<T>>::get(collection_id)?;2494                2495                let mut sponsor_transfer = false;2496                if collection.sponsor_confirmed {24972498                    let collection_limits = collection.limits;2499                    let collection_mode = collection.mode;2500    2501                    // sponsor timeout2502                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2503                    sponsor_transfer = match collection_mode {2504                        CollectionMode::NFT => {2505    2506                            // get correct limit2507                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2508                                collection_limits.sponsor_transfer_timeout2509                            } else {2510                                ChainLimit::get().nft_sponsor_transfer_timeout2511                            };2512    2513                            let mut sponsored = true;2514                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2515                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2516                                let limit_time = last_tx_block + limit.into();2517                                if block_number <= limit_time {2518                                    sponsored = false;2519                                }2520                            }2521                            if sponsored {2522                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2523                            }25242525                            sponsored2526                        }2527                        CollectionMode::Fungible(_) => {2528    2529                            // get correct limit2530                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2531                                collection_limits.sponsor_transfer_timeout2532                            } else {2533                                ChainLimit::get().fungible_sponsor_transfer_timeout2534                            };2535    2536                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2537                            let mut sponsored = true;2538                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2539                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2540                                let limit_time = last_tx_block + limit.into();2541                                if block_number <= limit_time {2542                                    sponsored = false;2543                                }2544                            }2545                            if sponsored {2546                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2547                            }25482549                            sponsored2550                        }2551                        CollectionMode::ReFungible => {2552    2553                            // get correct limit2554                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2555                                collection_limits.sponsor_transfer_timeout2556                            } else {2557                                ChainLimit::get().refungible_sponsor_transfer_timeout2558                            };2559    2560                            let mut sponsored = true;2561                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2562                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2563                                let limit_time = last_tx_block + limit.into();2564                                if block_number <= limit_time {2565                                    sponsored = false;2566                                }2567                            }2568                            if sponsored {2569                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2570                            }25712572                            sponsored2573                        }2574                        _ => {2575                            false2576                        },2577                    };2578                }25792580                if !sponsor_transfer {2581                    None2582                } else {2583                    Some(collection.sponsor)2584                }2585            }25862587            Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2588                let mut sponsor_metadata_changes = false;25892590                let collection = <CollectionById<T>>::get(collection_id)?;25912592                if2593                    collection.sponsor_confirmed &&2594                    // Can't sponsor fungible collection, this tx will be rejected2595                    // as invalid2596                    !matches!(collection.mode, CollectionMode::Fungible(_)) &&2597                    data.len() <= collection.limits.sponsored_data_size as usize2598                {2599                    if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2600                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;26012602                        if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2603                            .map(|last_block| block_number - last_block > rate_limit)2604                            .unwrap_or(true) 2605                        {2606                            sponsor_metadata_changes = true;2607                            <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2608                        }2609                    }2610                }26112612                if !sponsor_metadata_changes {2613                    None2614                } else {2615                    Some(collection.sponsor)2616                }2617            }26182619            _ => None,2620        })();26212622        match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {2623            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {26242625                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());26262627                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2628                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2629                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2630                  2631                if !owned_contract && white_list_enabled {2632                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2633                        return Err(InvalidTransaction::Call.into());2634                    }2635                }2636            },2637            _ => {},2638        }26392640        // Sponsor smart contracts2641        sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {26422643            // On instantiation: set the contract owner2644            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {26452646                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2647                    &who,2648                    code_hash,2649                    salt,2650                );2651                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());26522653                None2654            },26552656            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2657            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {26582659                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());26602661                let mut sponsor_transfer = false;2662                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2663                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2664                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2665                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2666                    let limit_time = last_tx_block + rate_limit;26672668                    if block_number >= limit_time {2669                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2670                        sponsor_transfer = true;2671                    }2672                } else {2673                    sponsor_transfer = false;2674                }2675               2676                if sponsor_transfer {2677                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2678                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2679                            return Some(called_contract);2680                        }2681                    }2682                }26832684                None2685            },26862687            _ => None,2688        });26892690        let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());26912692		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2693			.map(|i| (fee, i))2694    }2695}269626972698impl<T: Config + Send + Sync> SignedExtension2699    for ChargeTransactionPayment<T>2700where2701    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2702    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2703    T::AccountId: AsRef<[u8]>,2704    T::AccountId: UncheckedFrom<T::Hash>,2705{2706    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2707    type AccountId = T::AccountId;2708    type Call = T::Call;2709    type AdditionalSigned = ();2710    type Pre = (2711        // tip2712        BalanceOf<T>,2713        // who pays fee2714        Self::AccountId,2715		// imbalance resulting from withdrawing the fee2716		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2717    );2718    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2719        Ok(())2720    }27212722    fn validate(2723        &self,2724        who: &Self::AccountId,2725        call: &Self::Call,2726        info: &DispatchInfoOf<Self::Call>,2727        len: usize,2728    ) -> TransactionValidity {2729		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2730		Ok(ValidTransaction {2731			priority: Self::get_priority(len, info, fee),2732			..Default::default()2733		})2734    }27352736    fn pre_dispatch(2737        self,2738        who: &Self::AccountId,2739        call: &Self::Call,2740        info: &DispatchInfoOf<Self::Call>,2741        len: usize,2742    ) -> Result<Self::Pre, TransactionValidityError> {2743        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2744        Ok((self.0, who.clone(), imbalance))2745    }27462747    fn post_dispatch(2748        pre: Self::Pre,2749        info: &DispatchInfoOf<Self::Call>,2750        post_info: &PostDispatchInfoOf<Self::Call>,2751        len: usize,2752        _result: &DispatchResult,2753    ) -> Result<(), TransactionValidityError> {2754		let (tip, who, imbalance) = pre;2755		let actual_fee = transaction_payment::Module::<T>::compute_actual_fee(2756			len as u32,2757			info,2758			post_info,2759			tip,2760		);2761		<T as transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2762		Ok(())2763    }2764}27652766// #endregion
modifiedruntime/src/chain_extension.rsdiffbeforeafterboth
--- a/runtime/src/chain_extension.rs
+++ b/runtime/src/chain_extension.rs
@@ -60,7 +60,9 @@
                 }
                 let recipient = AccountId32::from(bytes_rec);
 
-                match pallet_nft::Module::<Runtime>::transfer_internal(sender, recipient, input.collection_id, input.token_id, input.amount) {
+                let collection = pallet_nft::Module::<Runtime>::get_collection(input.collection_id)?;
+
+                match pallet_nft::Module::<Runtime>::transfer_internal(sender, recipient, &collection, input.token_id, input.amount) {
                     Ok(_) => Ok(RetVal::Converging(func_id)),
                     _ => Err(DispatchError::Other("Transfer error"))
                 }
modifiedruntime_types.jsondiffbeforeafterboth
--- a/runtime_types.json
+++ b/runtime_types.json
@@ -31,7 +31,8 @@
       "ConstData": "Vec<u8>",
       "VariableData": "Vec<u8>"
     },
-    "CollectionType": {
+    "Collection": {
+      "Id": "CollectionId",
       "Owner": "AccountId",
       "Mode": "CollectionMode",
       "Access": "AccessMode",