git.delta.rocks / unique-network / refs/commits / 8d3b0d18cfc9

difftreelog

refactor remove sponsor permissions check

Yaroslav Bolyukin2021-03-03parent: #66c85be.patch.diff
in: master
As sponsor should have no unique permissions in sponsored collection

1 file changed

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_value(), 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        /// Schema data size limit bound exceeded392        SchemaDataLimitExceeded,393        /// Maximum refungibility exceeded394        WrongRefungiblePieces395	}396}397398pub trait Config: system::Config + Sized + transaction_payment::Config + pallet_contracts::Config {399    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;400401    /// Weight information for extrinsics in this pallet.402	type WeightInfo: WeightInfo;403}404405#[cfg(feature = "runtime-benchmarks")]406mod benchmarking;407408// #endregion409410decl_storage! {411    trait Store for Module<T: Config> as Nft {412413        // Private members414        NextCollectionID: CollectionId;415        CreatedCollectionCount: u32;416        ChainVersion: u64;417        ItemListIndex: map hasher(identity) CollectionId => TokenId;418419        // Chain limits struct420        pub ChainLimit get(fn chain_limit) config(): ChainLimits;421422        // Bound counters423        CollectionCount: u32;424        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;425426        // Basic collections427        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => Option<CollectionType<T>> = None;428        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;429        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;430431        /// Balance owner per collection map432        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;433434        /// second parameter: item id + owner account id + spender account id435        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;436437        /// Item collections438        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;439        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;440        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;441442        /// Index list443        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;444445        /// Tokens transfer baskets446        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;447        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;448        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;449        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;450451        /// Variable metadata sponsoring452        pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Option<T::BlockNumber> = None;453454        // Contract Sponsorship and Ownership455        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;456        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;457        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;458        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;459        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 460        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 461    }462    add_extra_genesis {463        build(|config: &GenesisConfig<T>| {464            // Modification of storage465            for (_num, _c) in &config.collection {466                <Module<T>>::init_collection(_c);467            }468469            for (_num, _c, _i) in &config.nft_item_id {470                <Module<T>>::init_nft_token(*_c, _i);471            }472473            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {474                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);475            }476477            for (_num, _c, _i) in &config.refungible_item_id {478                <Module<T>>::init_refungible_token(*_c, _i);479            }480        })481    }482}483484decl_event!(485    pub enum Event<T>486    where487        AccountId = <T as system::Config>::AccountId,488    {489        /// New collection was created490        /// 491        /// # Arguments492        /// 493        /// * collection_id: Globally unique identifier of newly created collection.494        /// 495        /// * mode: [CollectionMode] converted into u8.496        /// 497        /// * account_id: Collection owner.498        Created(CollectionId, u8, AccountId),499500        /// New item was created.501        /// 502        /// # Arguments503        /// 504        /// * collection_id: Id of the collection where item was created.505        /// 506        /// * item_id: Id of an item. Unique within the collection.507        ItemCreated(CollectionId, TokenId),508509        /// Collection item was burned.510        /// 511        /// # Arguments512        /// 513        /// collection_id.514        /// 515        /// item_id: Identifier of burned NFT.516        ItemDestroyed(CollectionId, TokenId),517    }518);519520decl_module! {521    pub struct Module<T: Config> for enum Call 522    where 523        origin: T::Origin524    {525        fn deposit_event() = default;526        type Error = Error<T>;527528        fn on_initialize(now: T::BlockNumber) -> Weight {529530            if ChainVersion::get() < 2531            {532                let value = NextCollectionID::get();533                CreatedCollectionCount::put(value);534                ChainVersion::put(2);535            }536537            0538        }539540        /// 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.541        /// 542        /// # Permissions543        /// 544        /// * Anyone.545        /// 546        /// # Arguments547        /// 548        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.549        /// 550        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.551        /// 552        /// * token_prefix: UTF-8 string with token prefix.553        /// 554        /// * mode: [CollectionMode] collection type and type dependent data.555        // returns collection ID556        #[weight = <T as Config>::WeightInfo::create_collection()]557        pub fn create_collection(origin,558                                 collection_name: Vec<u16>,559                                 collection_description: Vec<u16>,560                                 token_prefix: Vec<u8>,561                                 mode: CollectionMode) -> DispatchResult {562563            // Anyone can create a collection564            let who = ensure_signed(origin)?;565566            let decimal_points = match mode {567                CollectionMode::Fungible(points) => points,568                _ => 0569            };570571            // bound Total number of collections572            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);573574            // check params575            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);576            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);577            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);578            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);579580            // Generate next collection ID581            let next_id = CreatedCollectionCount::get()582                .checked_add(1)583                .ok_or(Error::<T>::NumOverflow)?;584585            // bound counter586            let total = CollectionCount::get()587                .checked_add(1)588                .ok_or(Error::<T>::NumOverflow)?;589590            CreatedCollectionCount::put(next_id);591            CollectionCount::put(total);592593            // Create new collection594            let new_collection = CollectionType {595                owner: who.clone(),596                name: collection_name,597                mode: mode.clone(),598                mint_mode: false,599                access: AccessMode::Normal,600                description: collection_description,601                decimal_points: decimal_points,602                token_prefix: token_prefix,603                offchain_schema: Vec::new(),604                schema_version: SchemaVersion::ImageURL,605                sponsor: T::AccountId::default(),606                sponsor_confirmed: false,607                variable_on_chain_schema: Vec::new(),608                const_on_chain_schema: Vec::new(),609                limits: CollectionLimits::default()610            };611612            // Add new collection to map613            <Collection<T>>::insert(next_id, new_collection);614615            // call event616            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));617618            Ok(())619        }620621        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.622        /// 623        /// # Permissions624        /// 625        /// * Collection Owner.626        /// 627        /// # Arguments628        /// 629        /// * collection_id: collection to destroy.630        #[weight = <T as Config>::WeightInfo::destroy_collection()]631        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {632633            let sender = ensure_signed(origin)?;634            Self::check_owner_permissions(collection_id, sender)?;635636            <AddressTokens<T>>::remove_prefix(collection_id);637            <Allowances<T>>::remove_prefix(collection_id);638            <Balance<T>>::remove_prefix(collection_id);639            <ItemListIndex>::remove(collection_id);640            <AdminList<T>>::remove(collection_id);641            <Collection<T>>::remove(collection_id);642            <WhiteList<T>>::remove_prefix(collection_id);643644            <NftItemList<T>>::remove_prefix(collection_id);645            <FungibleItemList<T>>::remove_prefix(collection_id);646            <ReFungibleItemList<T>>::remove_prefix(collection_id);647648            <NftTransferBasket<T>>::remove_prefix(collection_id);649            <FungibleTransferBasket<T>>::remove_prefix(collection_id);650            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);651652            <VariableMetaDataBasket<T>>::remove_prefix(collection_id);653654            if CollectionCount::get() > 0655            {656                // bound couter657                let total = CollectionCount::get()658                    .checked_sub(1)659                    .ok_or(Error::<T>::NumOverflow)?;660661                CollectionCount::put(total);662            }663664            Ok(())665        }666667        /// Add an address to white list.668        /// 669        /// # Permissions670        /// 671        /// * Collection Owner672        /// * Collection Admin673        /// 674        /// # Arguments675        /// 676        /// * collection_id.677        /// 678        /// * address.679        #[weight = <T as Config>::WeightInfo::add_to_white_list()]680        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{681682            let sender = ensure_signed(origin)?;683            Self::check_owner_or_admin_permissions(collection_id, sender)?;684685            <WhiteList<T>>::insert(collection_id, address, true);686            687            Ok(())688        }689690        /// Remove an address from white list.691        /// 692        /// # Permissions693        /// 694        /// * Collection Owner695        /// * Collection Admin696        /// 697        /// # Arguments698        /// 699        /// * collection_id.700        /// 701        /// * address.702        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]703        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{704705            let sender = ensure_signed(origin)?;706            Self::check_owner_or_admin_permissions(collection_id, sender)?;707708            <WhiteList<T>>::remove(collection_id, address);709710            Ok(())711        }712713        /// Toggle between normal and white list access for the methods with access for `Anyone`.714        /// 715        /// # Permissions716        /// 717        /// * Collection Owner.718        /// 719        /// # Arguments720        /// 721        /// * collection_id.722        /// 723        /// * mode: [AccessMode]724        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]725        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult726        {727            let sender = ensure_signed(origin)?;728729            Self::check_owner_permissions(collection_id, sender)?;730            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();731            target_collection.access = mode;732            <Collection<T>>::insert(collection_id, target_collection);733734            Ok(())735        }736737        /// Allows Anyone to create tokens if:738        /// * White List is enabled, and739        /// * Address is added to white list, and740        /// * This method was called with True parameter741        /// 742        /// # Permissions743        /// * Collection Owner744        ///745        /// # Arguments746        /// 747        /// * collection_id.748        /// 749        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.750        #[weight = <T as Config>::WeightInfo::set_mint_permission()]751        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult752        {753            let sender = ensure_signed(origin)?;754755            Self::check_owner_permissions(collection_id, sender)?;756            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();757            target_collection.mint_mode = mint_permission;758            <Collection<T>>::insert(collection_id, target_collection);759760            Ok(())761        }762763        /// Change the owner of the collection.764        /// 765        /// # Permissions766        /// 767        /// * Collection Owner.768        /// 769        /// # Arguments770        /// 771        /// * collection_id.772        /// 773        /// * new_owner.774        #[weight = <T as Config>::WeightInfo::change_collection_owner()]775        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {776777            let sender = ensure_signed(origin)?;778            Self::check_owner_permissions(collection_id, sender)?;779            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();780            target_collection.owner = new_owner;781            <Collection<T>>::insert(collection_id, target_collection);782783            Ok(())784        }785786        /// Adds an admin of the Collection.787        /// 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. 788        /// 789        /// # Permissions790        /// 791        /// * Collection Owner.792        /// * Collection Admin.793        /// 794        /// # Arguments795        /// 796        /// * collection_id: ID of the Collection to add admin for.797        /// 798        /// * new_admin_id: Address of new admin to add.799        #[weight = <T as Config>::WeightInfo::add_collection_admin()]800        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {801802            let sender = ensure_signed(origin)?;803            Self::check_owner_or_admin_permissions(collection_id, sender)?;804            let mut admin_arr: Vec<T::AccountId> = Vec::new();805806            if <AdminList<T>>::contains_key(collection_id)807            {808                admin_arr = <AdminList<T>>::get(collection_id);809                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);810            }811812            // Number of collection admins813            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);814815            admin_arr.push(new_admin_id);816            <AdminList<T>>::insert(collection_id, admin_arr);817818            Ok(())819        }820821        /// 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.822        ///823        /// # Permissions824        /// 825        /// * Collection Owner.826        /// * Collection Admin.827        /// 828        /// # Arguments829        /// 830        /// * collection_id: ID of the Collection to remove admin for.831        /// 832        /// * account_id: Address of admin to remove.833        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]834        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {835836            let sender = ensure_signed(origin)?;837            Self::check_owner_or_admin_permissions(collection_id, sender)?;838            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);839840            let mut admin_arr = <AdminList<T>>::get(collection_id);841            admin_arr.retain(|i| *i != account_id);842            <AdminList<T>>::insert(collection_id, admin_arr);843844            Ok(())845        }846847        /// # Permissions848        /// 849        /// * Collection Owner850        /// 851        /// # Arguments852        /// 853        /// * collection_id.854        /// 855        /// * new_sponsor.856        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]857        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {858859            let sender = ensure_signed(origin)?;860861            let mut target_collection = <Collection<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?;862            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);863864            target_collection.sponsor = new_sponsor;865            target_collection.sponsor_confirmed = false;866            <Collection<T>>::insert(collection_id, target_collection);867868            Ok(())869        }870871        /// # Permissions872        /// 873        /// * Sponsor.874        /// 875        /// # Arguments876        /// 877        /// * collection_id.878        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]879        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {880881            let sender = ensure_signed(origin)?;882883            let mut target_collection = <Collection<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?;884            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);885886            target_collection.sponsor_confirmed = true;887            <Collection<T>>::insert(collection_id, target_collection);888889            Ok(())890        }891892        /// Switch back to pay-per-own-transaction model.893        ///894        /// # Permissions895        ///896        /// * Collection owner.897        /// 898        /// # Arguments899        /// 900        /// * collection_id.901        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]902        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {903904            let sender = ensure_signed(origin)?;905906            let mut target_collection = <Collection<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?;907            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);908909            target_collection.sponsor = T::AccountId::default();910            target_collection.sponsor_confirmed = false;911            <Collection<T>>::insert(collection_id, target_collection);912913            Ok(())914        }915916        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.917        /// 918        /// # Permissions919        /// 920        /// * Collection Owner.921        /// * Collection Admin.922        /// * Anyone if923        ///     * White List is enabled, and924        ///     * Address is added to white list, and925        ///     * MintPermission is enabled (see SetMintPermission method)926        /// 927        /// # Arguments928        /// 929        /// * collection_id: ID of the collection.930        /// 931        /// * owner: Address, initial owner of the NFT.932        ///933        /// * data: Token data to store on chain.934        // #[weight =935        // (130_000_000 as Weight)936        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))937        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))938        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]939940        #[weight = <T as Config>::WeightInfo::create_item(data.len())]941        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {942943            let sender = ensure_signed(origin)?;944945            Self::collection_exists(collection_id)?;946947            let target_collection = <Collection<T>>::get(collection_id).unwrap();948949            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;950            Self::validate_create_item_args(&target_collection, &data)?;951            Self::create_item_no_validation(collection_id, owner, data)?;952953            Ok(())954        }955956        /// This method creates multiple instances of NFT Collection created with CreateCollection method.957        /// 958        /// # Permissions959        /// 960        /// * Collection Owner.961        /// * Collection Admin.962        /// * Anyone if963        ///     * White List is enabled, and964        ///     * Address is added to white list, and965        ///     * MintPermission is enabled (see SetMintPermission method)966        /// 967        /// # Arguments968        /// 969        /// * collection_id: ID of the collection.970        /// 971        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].972        /// 973        /// * owner: Address, initial owner of the NFT.974        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()975                               .map(|data| { data.len() })976                               .sum())]977        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {978979            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);980            let sender = ensure_signed(origin)?;981982            Self::collection_exists(collection_id)?;983            let target_collection = <Collection<T>>::get(collection_id).unwrap();984985            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;986987            for data in &items_data {988                Self::validate_create_item_args(&target_collection, data)?;989            }990            for data in &items_data {991                Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?;992            }993994            Ok(())995        }996997        /// Destroys a concrete instance of NFT.998        /// 999        /// # Permissions1000        /// 1001        /// * Collection Owner.1002        /// * Collection Admin.1003        /// * Current NFT Owner.1004        /// 1005        /// # Arguments1006        /// 1007        /// * collection_id: ID of the collection.1008        /// 1009        /// * item_id: ID of NFT to burn.1010        #[weight = <T as Config>::WeightInfo::burn_item()]1011        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10121013            let sender = ensure_signed(origin)?;1014            Self::collection_exists(collection_id)?;10151016            // Transfer permissions check1017            let target_collection = <Collection<T>>::get(collection_id).unwrap();1018            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1019                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1020                Error::<T>::NoPermission);10211022            if target_collection.access == AccessMode::WhiteList {1023                Self::check_white_list(collection_id, &sender)?;1024            }10251026            match target_collection.mode1027            {1028                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1029                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1030                CollectionMode::ReFungible  => Self::burn_refungible_item(collection_id, item_id, &sender)?,1031                _ => ()1032            };10331034            // call event1035            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10361037            Ok(())1038        }10391040        /// Change ownership of the token.1041        /// 1042        /// # Permissions1043        /// 1044        /// * Collection Owner1045        /// * Collection Admin1046        /// * Current NFT owner1047        ///1048        /// # Arguments1049        /// 1050        /// * recipient: Address of token recipient.1051        /// 1052        /// * collection_id.1053        /// 1054        /// * item_id: ID of the item1055        ///     * Non-Fungible Mode: Required.1056        ///     * Fungible Mode: Ignored.1057        ///     * Re-Fungible Mode: Required.1058        /// 1059        /// * value: Amount to transfer.1060        ///     * Non-Fungible Mode: Ignored1061        ///     * Fungible Mode: Must specify transferred amount1062        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1063        #[weight = <T as Config>::WeightInfo::transfer()]1064        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1065            let sender = ensure_signed(origin)?;1066            Self::transfer_internal(sender, recipient, collection_id, item_id, value)1067        }10681069        /// Set, change, or remove approved address to transfer the ownership of the NFT.1070        /// 1071        /// # Permissions1072        /// 1073        /// * Collection Owner1074        /// * Collection Admin1075        /// * Current NFT owner1076        /// 1077        /// # Arguments1078        /// 1079        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1080        /// 1081        /// * collection_id.1082        /// 1083        /// * item_id: ID of the item.1084        #[weight = <T as Config>::WeightInfo::approve()]1085        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {10861087            let sender = ensure_signed(origin)?;10881089            Self::collection_exists(collection_id)?;1090            Self::token_exists(collection_id, item_id, &sender)?;10911092            // Transfer permissions check1093            let target_collection = <Collection<T>>::get(collection_id).unwrap();1094            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1095                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1096                Error::<T>::NoPermission);10971098            if target_collection.access == AccessMode::WhiteList {1099                Self::check_white_list(collection_id, &sender)?;1100                Self::check_white_list(collection_id, &spender)?;1101            }11021103            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1104            let mut allowance: u128 = amount;1105            if allowance_exists {1106                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1107            }1108            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11091110            Ok(())1111        }1112        1113        /// 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.1114        /// 1115        /// # Permissions1116        /// * Collection Owner1117        /// * Collection Admin1118        /// * Current NFT owner1119        /// * Address approved by current NFT owner1120        /// 1121        /// # Arguments1122        /// 1123        /// * from: Address that owns token.1124        /// 1125        /// * recipient: Address of token recipient.1126        /// 1127        /// * collection_id.1128        /// 1129        /// * item_id: ID of the item.1130        /// 1131        /// * value: Amount to transfer.1132        #[weight = <T as Config>::WeightInfo::transfer_from()]1133        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11341135            let sender = ensure_signed(origin)?;1136            let mut appoved_transfer = false;11371138            // Check approval1139            let mut approval: u128 = 0;1140            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1141                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1142                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1143                appoved_transfer = true;1144            }11451146            let target_collection = <Collection<T>>::get(collection_id).unwrap();11471148            // Limits check1149            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11501151            // Transfer permissions check         1152            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1153                Error::<T>::NoPermission);11541155            if target_collection.access == AccessMode::WhiteList {1156                Self::check_white_list(collection_id, &sender)?;1157                Self::check_white_list(collection_id, &recipient)?;1158            }11591160            // Reduce approval by transferred amount or remove if remaining approval drops to 01161            if approval.checked_sub(value).unwrap_or(0) > 0 {1162                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1163            }1164            else {1165                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1166            }11671168            match target_collection.mode1169            {1170                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1171                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1172                CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1173                _ => ()1174            };11751176            Ok(())1177        }11781179        // #[weight = 0]1180        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11811182        //     // let no_perm_mes = "You do not have permissions to modify this collection";1183        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1184        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1185        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11861187        //     // // on_nft_received  call11881189        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;11901191        //     Ok(())1192        // }11931194        /// Set off-chain data schema.1195        /// 1196        /// # Permissions1197        /// 1198        /// * Collection Owner1199        /// * Collection Admin1200        /// 1201        /// # Arguments1202        /// 1203        /// * collection_id.1204        /// 1205        /// * schema: String representing the offchain data schema.1206        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1207        pub fn set_variable_meta_data (1208            origin,1209            collection_id: CollectionId,1210            item_id: TokenId,1211            data: Vec<u8>1212        ) -> DispatchResult {1213            let sender = ensure_signed(origin)?;1214            1215            Self::collection_exists(collection_id)?;1216            Self::token_exists(collection_id, item_id, &sender)?;12171218            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12191220            // Modify permissions check1221            let target_collection = <Collection<T>>::get(collection_id).unwrap();1222            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1223                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1224                Error::<T>::NoPermission);12251226            match target_collection.mode1227            {1228                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1229                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1230                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1231                _ => fail!(Error::<T>::UnexpectedCollectionType)1232            };12331234            Ok(())1235        }1236 1237        /// Set schema standard1238        /// ImageURL1239        /// Unique1240        /// 1241        /// # Permissions1242        /// 1243        /// * Collection Owner1244        /// * Collection Admin1245        /// 1246        /// # Arguments1247        /// 1248        /// * collection_id.1249        /// 1250        /// * schema: SchemaVersion: enum1251        #[weight = <T as Config>::WeightInfo::set_schema_version()]1252        pub fn set_schema_version(1253            origin,1254            collection_id: CollectionId,1255            version: SchemaVersion1256        ) -> DispatchResult {1257            let sender = ensure_signed(origin)?;1258            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1259            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();1260            target_collection.schema_version = version;1261            <Collection<T>>::insert(collection_id, target_collection);12621263            Ok(())1264        }12651266        /// Set off-chain data schema.1267        /// 1268        /// # Permissions1269        /// 1270        /// * Collection Owner1271        /// * Collection Admin1272        /// 1273        /// # Arguments1274        /// 1275        /// * collection_id.1276        /// 1277        /// * schema: String representing the offchain data schema.1278        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1279        pub fn set_offchain_schema(1280            origin,1281            collection_id: CollectionId,1282            schema: Vec<u8>1283        ) -> DispatchResult {1284            let sender = ensure_signed(origin)?;1285            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12861287            // check schema limit1288            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");12891290            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();1291            target_collection.offchain_schema = schema;1292            <Collection<T>>::insert(collection_id, target_collection);12931294            Ok(())1295        }12961297        /// Set const on-chain data schema.1298        /// 1299        /// # Permissions1300        /// 1301        /// * Collection Owner1302        /// * Collection Admin1303        /// 1304        /// # Arguments1305        /// 1306        /// * collection_id.1307        /// 1308        /// * schema: String representing the const on-chain data schema.1309        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1310        pub fn set_const_on_chain_schema (1311            origin,1312            collection_id: CollectionId,1313            schema: Vec<u8>1314        ) -> DispatchResult {1315            let sender = ensure_signed(origin)?;1316            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13171318            // check schema limit1319            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");13201321            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();1322            target_collection.const_on_chain_schema = schema;1323            <Collection<T>>::insert(collection_id, target_collection);13241325            Ok(())1326        }13271328        /// Set variable on-chain data schema.1329        /// 1330        /// # Permissions1331        /// 1332        /// * Collection Owner1333        /// * Collection Admin1334        /// 1335        /// # Arguments1336        /// 1337        /// * collection_id.1338        /// 1339        /// * schema: String representing the variable on-chain data schema.1340        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1341        pub fn set_variable_on_chain_schema (1342            origin,1343            collection_id: CollectionId,1344            schema: Vec<u8>1345        ) -> DispatchResult {1346            let sender = ensure_signed(origin)?;1347            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13481349            // check schema limit1350            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");13511352            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();1353            target_collection.variable_on_chain_schema = schema;1354            <Collection<T>>::insert(collection_id, target_collection);13551356            Ok(())1357        }13581359        // Sudo permissions function1360        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1361        pub fn set_chain_limits(1362            origin,1363            limits: ChainLimits1364        ) -> DispatchResult {13651366            #[cfg(not(feature = "runtime-benchmarks"))]1367            ensure_root(origin)?;13681369            <ChainLimit>::put(limits);1370            Ok(())1371        }13721373        /// Enable smart contract self-sponsoring.1374        /// 1375        /// # Permissions1376        /// 1377        /// * Contract Owner1378        /// 1379        /// # Arguments1380        /// 1381        /// * contract address1382        /// * enable flag1383        /// 1384        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1385        pub fn enable_contract_sponsoring(1386            origin,1387            contract_address: T::AccountId,1388            enable: bool1389        ) -> DispatchResult {13901391            let sender = ensure_signed(origin)?;13921393            #[cfg(feature = "runtime-benchmarks")]1394            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13951396            Self::ensure_contract_owned(sender, &contract_address)?;13971398            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1399            Ok(())1400        }14011402        /// Set the rate limit for contract sponsoring to specified number of blocks.1403        /// 1404        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1405        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1406        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1407        /// from contract endowment if there are at least B blocks between such transactions. 1408        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1409        /// 1410        /// # Permissions1411        /// 1412        /// * Contract Owner1413        /// 1414        /// # Arguments1415        /// 1416        /// -`contract_address`: Address of the contract to sponsor1417        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1418        /// 1419        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1420        pub fn set_contract_sponsoring_rate_limit(1421            origin,1422            contract_address: T::AccountId,1423            rate_limit: T::BlockNumber1424        ) -> DispatchResult {1425            let sender = ensure_signed(origin)?;14261427            #[cfg(feature = "runtime-benchmarks")]1428            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14291430            Self::ensure_contract_owned(sender, &contract_address)?;1431            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1432            Ok(())1433        }14341435        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1436        /// 1437        /// # Permissions1438        /// 1439        /// * Address that deployed smart contract.1440        /// 1441        /// # Arguments1442        /// 1443        /// -`contract_address`: Address of the contract.1444        /// 1445        /// - `enable`: .  1446        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1447        pub fn toggle_contract_white_list(1448            origin,1449            contract_address: T::AccountId,1450            enable: bool1451        ) -> DispatchResult {1452            let sender = ensure_signed(origin)?;14531454            #[cfg(feature = "runtime-benchmarks")]1455            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14561457            Self::ensure_contract_owned(sender, &contract_address)?;1458            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1459            Ok(())1460        }1461        1462        /// Add an address to smart contract white list.1463        /// 1464        /// # Permissions1465        /// 1466        /// * Address that deployed smart contract.1467        /// 1468        /// # Arguments1469        /// 1470        /// -`contract_address`: Address of the contract.1471        ///1472        /// -`account_address`: Address to add.1473        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1474        pub fn add_to_contract_white_list(1475            origin,1476            contract_address: T::AccountId,1477            account_address: T::AccountId1478        ) -> DispatchResult {1479            let sender = ensure_signed(origin)?;14801481            #[cfg(feature = "runtime-benchmarks")]1482            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1483            1484            Self::ensure_contract_owned(sender, &contract_address)?;      1485            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1486            Ok(())1487        }14881489        /// Remove an address from smart contract white list.1490        /// 1491        /// # Permissions1492        /// 1493        /// * Address that deployed smart contract.1494        /// 1495        /// # Arguments1496        /// 1497        /// -`contract_address`: Address of the contract.1498        ///1499        /// -`account_address`: Address to remove.1500        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1501        pub fn remove_from_contract_white_list(1502            origin,1503            contract_address: T::AccountId,1504            account_address: T::AccountId1505        ) -> DispatchResult {1506            let sender = ensure_signed(origin)?;15071508            #[cfg(feature = "runtime-benchmarks")]1509            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15101511            Self::ensure_contract_owned(sender, &contract_address)?;1512            <ContractWhiteList<T>>::remove(contract_address, account_address);1513            Ok(())1514        }15151516        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1517        pub fn set_collection_limits(1518            origin,1519            collection_id: u32,1520            limits: CollectionLimits<T::BlockNumber>,1521        ) -> DispatchResult {1522            let sender = ensure_signed(origin)?;1523            Self::check_owner_permissions(collection_id, sender.clone())?;1524            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();1525            let chain_limits = ChainLimit::get();1526            let climits = target_collection.limits;15271528            // collection bounds1529            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1530                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1531                Error::<T>::CollectionLimitBoundsExceeded);15321533            // token_limit   check  prev1534            ensure!(climits.token_limit > limits.token_limit && 1535                limits.token_limit <= chain_limits.account_token_ownership_limit, 1536                Error::<T>::AccountTokenLimitExceeded);15371538            target_collection.limits = limits;1539            <Collection<T>>::insert(collection_id, target_collection);15401541            Ok(())1542        } 1543    }1544}15451546impl<T: Config> Module<T> {15471548    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {15491550        let target_collection = <Collection<T>>::get(collection_id).unwrap();15511552        // Limits check1553        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;15541555        // Transfer permissions check1556        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1557            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1558            Error::<T>::NoPermission);15591560        if target_collection.access == AccessMode::WhiteList {1561            Self::check_white_list(collection_id, &sender)?;1562            Self::check_white_list(collection_id, &recipient)?;1563        }15641565        match target_collection.mode1566        {1567            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1568            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1569            CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1570            _ => ()1571        };15721573        Ok(())1574    }157515761577    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T>, recipient: &T::AccountId) -> DispatchResult {15781579        // check token limit and account token limit1580        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1581        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1582        1583        Ok(())1584    }15851586    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15871588        // check token limit and account token limit1589        let total_items: u32 = ItemListIndex::get(collection_id);1590        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1591        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1592        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15931594        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1595            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1596            Self::check_white_list(collection_id, owner)?;1597            Self::check_white_list(collection_id, sender)?;1598        }15991600        Ok(())1601    }16021603    fn validate_create_item_args(target_collection: &CollectionType<T>, data: &CreateItemData) -> DispatchResult {1604        match target_collection.mode1605        {1606            CollectionMode::NFT => {1607                if let CreateItemData::NFT(data) = data {1608                    // check sizes1609                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1610                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1611                } else {1612                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1613                }1614            },1615            CollectionMode::Fungible(_) => {1616                if let CreateItemData::Fungible(_) = data {1617                } else {1618                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1619                }1620            },1621            CollectionMode::ReFungible => {1622                if let CreateItemData::ReFungible(data) = data {16231624                    // check sizes1625                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1626                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16271628                    // Check refungibility limits1629                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1630                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1631                } else {1632                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1633                }1634            },1635            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1636        };16371638        Ok(())1639    }16401641    fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1642        match data1643        {1644            CreateItemData::NFT(data) => {1645                let item = NftItemType {1646                    owner,1647                    const_data: data.const_data,1648                    variable_data: data.variable_data1649                };16501651                Self::add_nft_item(collection_id, item)?;1652            },1653            CreateItemData::Fungible(data) => {1654                Self::add_fungible_item(collection_id, &owner, data.value)?;1655            },1656            CreateItemData::ReFungible(data) => {1657                let mut owner_list = Vec::new();1658                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});16591660                let item = ReFungibleItemType {1661                    owner: owner_list,1662                    const_data: data.const_data,1663                    variable_data: data.variable_data1664                };16651666                Self::add_refungible_item(collection_id, item)?;1667            }1668        };16691670        // call event1671        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16721673        Ok(())1674    }16751676    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {16771678        // Does new owner already have an account?1679        let mut balance: u128 = 0;1680        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1681            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1682        } 16831684        // Mint 1685        let item = FungibleItemType {1686            value: balance + value1687        };1688        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16891690        // Update balance1691        let new_balance = <Balance<T>>::get(collection_id, owner)1692            .checked_add(value)1693            .ok_or(Error::<T>::NumOverflow)?;1694        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);16951696        Ok(())1697    }16981699    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1700        let current_index = <ItemListIndex>::get(collection_id)1701            .checked_add(1)1702            .ok_or(Error::<T>::NumOverflow)?;1703        let itemcopy = item.clone();17041705        let value = item.owner.first().unwrap().fraction;1706        let owner = item.owner.first().unwrap().owner.clone();17071708        Self::add_token_index(collection_id, current_index, &owner)?;17091710        <ItemListIndex>::insert(collection_id, current_index);1711        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17121713        // Update balance1714        let new_balance = <Balance<T>>::get(collection_id, &owner)1715            .checked_add(value)1716            .ok_or(Error::<T>::NumOverflow)?;1717        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17181719        Ok(())1720    }17211722    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1723        let current_index = <ItemListIndex>::get(collection_id)1724            .checked_add(1)1725            .ok_or(Error::<T>::NumOverflow)?;17261727        let item_owner = item.owner.clone();1728        Self::add_token_index(collection_id, current_index, &item.owner)?;17291730        <ItemListIndex>::insert(collection_id, current_index);1731        <NftItemList<T>>::insert(collection_id, current_index, item);17321733        // Update balance1734        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1735            .checked_add(1)1736            .ok_or(Error::<T>::NumOverflow)?;1737        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17381739        Ok(())1740    }17411742    fn burn_refungible_item(1743        collection_id: CollectionId,1744        item_id: TokenId,1745        owner: &T::AccountId,1746    ) -> DispatchResult {1747        ensure!(1748            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1749            Error::<T>::TokenNotFound1750        );1751        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1752        let rft_balance = token1753            .owner1754            .iter()1755            .filter(|&i| i.owner == *owner)1756            .next()1757            .unwrap();1758        Self::remove_token_index(collection_id, item_id, owner)?;17591760        // update balance1761        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1762            .checked_sub(rft_balance.fraction)1763            .ok_or(Error::<T>::NumOverflow)?;1764        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);17651766        // Re-create owners list with sender removed1767        let index = token1768            .owner1769            .iter()1770            .position(|i| i.owner == *owner)1771            .unwrap();1772        token.owner.remove(index);1773        let owner_count = token.owner.len();17741775        // Burn the token completely if this was the last (only) owner1776        if owner_count == 0 {1777            <ReFungibleItemList<T>>::remove(collection_id, item_id);1778            <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1779        }1780        else {1781            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1782        }17831784        Ok(())1785    }17861787    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1788        ensure!(1789            <NftItemList<T>>::contains_key(collection_id, item_id),1790            Error::<T>::TokenNotFound1791        );1792        let item = <NftItemList<T>>::get(collection_id, item_id);1793        Self::remove_token_index(collection_id, item_id, &item.owner)?;17941795        // update balance1796        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1797            .checked_sub(1)1798            .ok_or(Error::<T>::NumOverflow)?;1799        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1800        <NftItemList<T>>::remove(collection_id, item_id);1801        <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18021803        Ok(())1804    }18051806    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1807        ensure!(1808            <FungibleItemList<T>>::contains_key(collection_id, owner),1809            Error::<T>::TokenNotFound1810        );1811        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1812        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18131814        // update balance1815        let new_balance = <Balance<T>>::get(collection_id, owner)1816            .checked_sub(value)1817            .ok_or(Error::<T>::NumOverflow)?;1818        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18191820        if balance.value - value > 0 {1821            balance.value -= value;1822            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1823        }1824        else {1825            <FungibleItemList<T>>::remove(collection_id, owner);1826        }18271828        Ok(())1829    }18301831    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1832        ensure!(1833            <Collection<T>>::contains_key(collection_id),1834            Error::<T>::CollectionNotFound1835        );1836        Ok(())1837    }18381839    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1840        Self::collection_exists(collection_id)?;18411842        let target_collection = <Collection<T>>::get(collection_id).unwrap();1843        ensure!(1844            subject == target_collection.owner,1845            Error::<T>::NoPermission1846        );18471848        Ok(())1849    }18501851    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1852        let target_collection = <Collection<T>>::get(collection_id).unwrap();1853        let mut result: bool = subject == target_collection.owner;1854        let exists = <AdminList<T>>::contains_key(collection_id);18551856        if !result & exists {1857            if <AdminList<T>>::get(collection_id).contains(&subject) {1858                result = true1859            }1860        }18611862        result1863    }18641865    fn check_owner_or_admin_permissions(1866        collection_id: CollectionId,1867        subject: T::AccountId,1868    ) -> DispatchResult {1869        Self::collection_exists(collection_id)?;1870        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18711872        ensure!(1873            result,1874            Error::<T>::NoPermission1875        );1876        Ok(())1877    }18781879    fn check_collection_sponsor(1880        collection_id: CollectionId,1881        subject: T::AccountId,1882    ) -> DispatchResult {1883        Self::collection_exists(collection_id)?;1884        let collection = <Collection<T>>::get(collection_id).unwrap();1885        1886        ensure!(1887            collection.sponsor_confirmed &&1888            collection.sponsor == subject,1889            Error::<T>::NoPermission,1890        );1891        Ok(())1892    }18931894    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1895        let target_collection = <Collection<T>>::get(collection_id).unwrap();18961897        match target_collection.mode {1898            CollectionMode::NFT => {1899                <NftItemList<T>>::get(collection_id, item_id).owner == subject1900            }1901            CollectionMode::Fungible(_) => {1902                <FungibleItemList<T>>::contains_key(collection_id, &subject)1903            }1904            CollectionMode::ReFungible => {1905                <ReFungibleItemList<T>>::get(collection_id, item_id)1906                    .owner1907                    .iter()1908                    .any(|i| i.owner == subject)1909            }1910            CollectionMode::Invalid => false,1911        }1912    }19131914    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1915        let mes = Error::<T>::AddresNotInWhiteList;1916        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);19171918        Ok(())1919    }19201921    /// Check if token exists. In case of Fungible, check if there is an entry for 1922    /// the owner in fungible balances double map1923    fn token_exists(1924        collection_id: CollectionId,1925        item_id: TokenId,1926        owner: &T::AccountId1927    ) -> DispatchResult {1928        let target_collection = <Collection<T>>::get(collection_id).unwrap();1929        let exists = match target_collection.mode1930        {1931            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1932            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),1933            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1934            _ => false1935        };19361937        ensure!(exists == true, Error::<T>::TokenNotFound);1938        Ok(())1939    }19401941    fn transfer_fungible(1942        collection_id: CollectionId,1943        value: u128,1944        owner: &T::AccountId,1945        recipient: &T::AccountId,1946    ) -> DispatchResult {1947        Self::token_exists(collection_id, 0, owner)?;19481949        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1950        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19511952        // Send balance to recipient (updates balanceOf of recipient)1953        Self::add_fungible_item(collection_id, recipient, value)?;19541955        // update balanceOf of sender1956        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);19571958        // Reduce or remove sender1959        if balance.value == value {1960            <FungibleItemList<T>>::remove(collection_id, owner);1961        }1962        else {1963            balance.value -= value;1964            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1965        }19661967        Ok(())1968    }19691970    fn transfer_refungible(1971        collection_id: CollectionId,1972        item_id: TokenId,1973        value: u128,1974        owner: T::AccountId,1975        new_owner: T::AccountId,1976    ) -> DispatchResult {1977        Self::token_exists(collection_id, item_id, &owner)?;19781979        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1980        let item = full_item1981            .owner1982            .iter()1983            .filter(|i| i.owner == owner)1984            .next()1985            .ok_or(Error::<T>::NumOverflow)?;1986        let amount = item.fraction;19871988        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19891990        // update balance1991        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1992            .checked_sub(value)1993            .ok_or(Error::<T>::NumOverflow)?;1994        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19951996        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1997            .checked_add(value)1998            .ok_or(Error::<T>::NumOverflow)?;1999        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20002001        let old_owner = item.owner.clone();2002        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20032004        // transfer2005        if amount == value && !new_owner_has_account {2006            // change owner2007            // new owner do not have account2008            let mut new_full_item = full_item.clone();2009            new_full_item2010                .owner2011                .iter_mut()2012                .find(|i| i.owner == owner)2013                .unwrap()2014                .owner = new_owner.clone();2015            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20162017            // update index collection2018            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2019        } else {2020            let mut new_full_item = full_item.clone();2021            new_full_item2022                .owner2023                .iter_mut()2024                .find(|i| i.owner == owner)2025                .unwrap()2026                .fraction -= value;20272028            // separate amount2029            if new_owner_has_account {2030                // new owner has account2031                new_full_item2032                    .owner2033                    .iter_mut()2034                    .find(|i| i.owner == new_owner)2035                    .unwrap()2036                    .fraction += value;2037            } else {2038                // new owner do not have account2039                new_full_item.owner.push(Ownership {2040                    owner: new_owner.clone(),2041                    fraction: value,2042                });2043                Self::add_token_index(collection_id, item_id, &new_owner)?;2044            }20452046            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2047        }20482049        Ok(())2050    }20512052    fn transfer_nft(2053        collection_id: CollectionId,2054        item_id: TokenId,2055        sender: T::AccountId,2056        new_owner: T::AccountId,2057    ) -> DispatchResult {2058        Self::token_exists(collection_id, item_id, &sender)?;20592060        let mut item = <NftItemList<T>>::get(collection_id, item_id);20612062        ensure!(2063            sender == item.owner,2064            Error::<T>::MustBeTokenOwner2065        );20662067        // update balance2068        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2069            .checked_sub(1)2070            .ok_or(Error::<T>::NumOverflow)?;2071        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20722073        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2074            .checked_add(1)2075            .ok_or(Error::<T>::NumOverflow)?;2076        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20772078        // change owner2079        let old_owner = item.owner.clone();2080        item.owner = new_owner.clone();2081        <NftItemList<T>>::insert(collection_id, item_id, item);20822083        // update index collection2084        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;20852086        Ok(())2087    }2088    2089    fn set_re_fungible_variable_data(2090        collection_id: CollectionId,2091        item_id: TokenId,2092        data: Vec<u8>2093    ) -> DispatchResult {2094        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20952096        item.variable_data = data;20972098        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20992100        Ok(())2101    }21022103    fn set_nft_variable_data(2104        collection_id: CollectionId,2105        item_id: TokenId,2106        data: Vec<u8>2107    ) -> DispatchResult {2108        let mut item = <NftItemList<T>>::get(collection_id, item_id);2109        2110        item.variable_data = data;21112112        <NftItemList<T>>::insert(collection_id, item_id, item);2113        2114        Ok(())2115    }21162117    fn init_collection(item: &CollectionType<T>) {2118        // check params2119        assert!(2120            item.decimal_points <= MAX_DECIMAL_POINTS,2121            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2122        );2123        assert!(2124            item.name.len() <= 64,2125            "Collection name can not be longer than 63 char"2126        );2127        assert!(2128            item.name.len() <= 256,2129            "Collection description can not be longer than 255 char"2130        );2131        assert!(2132            item.token_prefix.len() <= 16,2133            "Token prefix can not be longer than 15 char"2134        );21352136        // Generate next collection ID2137        let next_id = CreatedCollectionCount::get()2138            .checked_add(1)2139            .unwrap();21402141        CreatedCollectionCount::put(next_id);2142    }21432144    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2145        let current_index = <ItemListIndex>::get(collection_id)2146            .checked_add(1)2147            .unwrap();21482149        let item_owner = item.owner.clone();2150        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21512152        <ItemListIndex>::insert(collection_id, current_index);21532154        // Update balance2155        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2156            .checked_add(1)2157            .unwrap();2158        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2159    }21602161    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2162        let current_index = <ItemListIndex>::get(collection_id)2163            .checked_add(1)2164            .unwrap();21652166        Self::add_token_index(collection_id, current_index, owner).unwrap();21672168        <ItemListIndex>::insert(collection_id, current_index);21692170        // Update balance2171        let new_balance = <Balance<T>>::get(collection_id, owner)2172            .checked_add(item.value)2173            .unwrap();2174        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2175    }21762177    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2178        let current_index = <ItemListIndex>::get(collection_id)2179            .checked_add(1)2180            .unwrap();21812182        let value = item.owner.first().unwrap().fraction;2183        let owner = item.owner.first().unwrap().owner.clone();21842185        Self::add_token_index(collection_id, current_index, &owner).unwrap();21862187        <ItemListIndex>::insert(collection_id, current_index);21882189        // Update balance2190        let new_balance = <Balance<T>>::get(collection_id, &owner)2191            .checked_add(value)2192            .unwrap();2193        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2194    }21952196    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {21972198        // add to account limit2199        if <AccountItemCount<T>>::contains_key(owner) {22002201            // bound Owned tokens by a single address2202            let count = <AccountItemCount<T>>::get(owner);2203            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);22042205            <AccountItemCount<T>>::insert(owner.clone(), count2206                .checked_add(1)2207                .ok_or(Error::<T>::NumOverflow)?);2208        }2209        else {2210            <AccountItemCount<T>>::insert(owner.clone(), 1);2211        }22122213        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2214        if list_exists {2215            let mut list = <AddressTokens<T>>::get(collection_id, owner);2216            let item_contains = list.contains(&item_index.clone());22172218            if !item_contains {2219                list.push(item_index.clone());2220            }22212222            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2223        } else {2224            let mut itm = Vec::new();2225            itm.push(item_index.clone());2226            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2227        }22282229        Ok(())2230    }22312232    fn remove_token_index(2233        collection_id: CollectionId,2234        item_index: TokenId,2235        owner: &T::AccountId,2236    ) -> DispatchResult {22372238        // update counter2239        <AccountItemCount<T>>::insert(owner.clone(), 2240            <AccountItemCount<T>>::get(owner)2241            .checked_sub(1)2242            .ok_or(Error::<T>::NumOverflow)?);224322442245        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2246        if list_exists {2247            let mut list = <AddressTokens<T>>::get(collection_id, owner);2248            let item_contains = list.contains(&item_index.clone());22492250            if item_contains {2251                list.retain(|&item| item != item_index);2252                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2253            }2254        }22552256        Ok(())2257    }22582259    fn move_token_index(2260        collection_id: CollectionId,2261        item_index: TokenId,2262        old_owner: &T::AccountId,2263        new_owner: &T::AccountId,2264    ) -> DispatchResult {2265        Self::remove_token_index(collection_id, item_index, old_owner)?;2266        Self::add_token_index(collection_id, item_index, new_owner)?;22672268        Ok(())2269    }2270    2271    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2272        if <ContractOwner<T>>::contains_key(contract.clone()) {2273            let owner = <ContractOwner<T>>::get(contract);2274            ensure!(account == owner, Error::<T>::NoPermission);2275        } else {2276            fail!(Error::<T>::NoPermission);2277        }22782279        Ok(())2280    }2281}22822283////////////////////////////////////////////////////////////////////////////////////////////////////2284// Economic models2285// #region22862287/// Fee multiplier.2288pub type Multiplier = FixedU128;22892290type BalanceOf<T> = <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::Balance;22912292/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2293/// in the queue.2294#[derive(Encode, Decode, Clone, Eq, PartialEq)]2295pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);22962297impl<T: Config + Send + Sync> sp_std::fmt::Debug 2298    for ChargeTransactionPayment<T>2299{2300	#[cfg(feature = "std")]2301	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2302		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2303	}2304	#[cfg(not(feature = "std"))]2305	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2306		Ok(())2307	}2308}23092310impl<T: Config> ChargeTransactionPayment<T>2311where2312    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2313    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2314    T::AccountId: AsRef<[u8]>,2315    T::AccountId: UncheckedFrom<T::Hash>,2316{2317    fn traditional_fee(2318        len: usize,2319        info: &DispatchInfoOf<T::Call>,2320        tip: BalanceOf<T>,2321    ) -> BalanceOf<T>2322    where2323        T::Call: Dispatchable<Info = DispatchInfo>,2324    {2325        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2326    }23272328	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2329        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2330        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2331        let len_saturation = max_block_length as u64 / (len as u64).max(1);2332        let coefficient: BalanceOf<T> = weight_saturation2333            .min(len_saturation)2334            .saturated_into::<BalanceOf<T>>();2335        final_fee2336            .saturating_mul(coefficient)2337            .saturated_into::<TransactionPriority>()2338    }23392340    fn withdraw_fee(2341        &self,2342        who: &T::AccountId,2343        call: &T::Call,2344        info: &DispatchInfoOf<T::Call>,2345        len: usize,2346	) -> Result<2347		(2348			BalanceOf<T>,2349			<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2350		),2351		TransactionValidityError,2352	> {2353        let tip = self.0;23542355        // Set fee based on call type. Creating collection costs 1 Unique.2356        // All other transactions have traditional fees so far2357        // let fee = match call.is_sub_type() {2358        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2359        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2360        //                                                 // _ => <BalanceOf<T>>::from(100)2361        // };2362        let fee = Self::traditional_fee(len, info, tip);23632364        // Only mess with balances if fee is not zero.2365        if fee.is_zero() {2366            return <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2367			.map(|i| (fee, i));2368        }23692370        // Determine who is paying transaction fee based on ecnomic model2371        // Parse call to extract collection ID and access collection sponsor2372        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2373            Some(Call::create_item(collection_id, _owner, _properties)) => {2374                let collection = <Collection<T>>::get(collection_id).unwrap();23752376                // sponsor timeout2377                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;23782379                let limit = collection.limits.sponsor_transfer_timeout;2380                let mut sponsored = true;2381                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2382                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2383                    let limit_time = last_tx_block + limit.into();2384                    if block_number <= limit_time {2385                        sponsored = false;2386                    }2387                }2388                if sponsored {2389                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2390                }23912392                // check free create limit2393                if (collection.limits.sponsored_data_size >= (_properties.len() as u32)) &&2394                   (collection.sponsor_confirmed) &&2395                   (sponsored)2396                {2397                    collection.sponsor2398                } else {2399                    T::AccountId::default()2400                }2401            }2402            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2403                let collection = <Collection<T>>::get(collection_id).unwrap();2404                2405                let mut sponsor_transfer = false;2406                if collection.sponsor_confirmed {24072408                    let collection_limits = collection.limits;2409                    let collection_mode = collection.mode;2410    2411                    // sponsor timeout2412                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2413                    sponsor_transfer = match collection_mode {2414                        CollectionMode::NFT => {2415    2416                            // get correct limit2417                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2418                                collection_limits.sponsor_transfer_timeout2419                            } else {2420                                ChainLimit::get().nft_sponsor_transfer_timeout2421                            };2422    2423                            let mut sponsored = true;2424                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2425                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2426                                let limit_time = last_tx_block + limit.into();2427                                if block_number <= limit_time {2428                                    sponsored = false;2429                                }2430                            }2431                            if sponsored {2432                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2433                            }24342435                            sponsored2436                        }2437                        CollectionMode::Fungible(_) => {2438    2439                            // get correct limit2440                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2441                                collection_limits.sponsor_transfer_timeout2442                            } else {2443                                ChainLimit::get().fungible_sponsor_transfer_timeout2444                            };2445    2446                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2447                            let mut sponsored = true;2448                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2449                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2450                                let limit_time = last_tx_block + limit.into();2451                                if block_number <= limit_time {2452                                    sponsored = false;2453                                }2454                            }2455                            if sponsored {2456                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2457                            }24582459                            sponsored2460                        }2461                        CollectionMode::ReFungible => {2462    2463                            // get correct limit2464                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2465                                collection_limits.sponsor_transfer_timeout2466                            } else {2467                                ChainLimit::get().refungible_sponsor_transfer_timeout2468                            };2469    2470                            let mut sponsored = true;2471                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2472                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2473                                let limit_time = last_tx_block + limit.into();2474                                if block_number <= limit_time {2475                                    sponsored = false;2476                                }2477                            }2478                            if sponsored {2479                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2480                            }24812482                            sponsored2483                        }2484                        _ => {2485                            false2486                        },2487                    };2488                }24892490                if !sponsor_transfer {2491                    T::AccountId::default()2492                } else {2493                    collection.sponsor2494                }2495            }24962497            Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2498                let mut sponsor_metadata_changes = false;24992500                let collection = <Collection<T>>::get(collection_id).unwrap();25012502                if2503                    collection.sponsor_confirmed &&2504                    // Can't sponsor fungible collection, this tx will be rejected2505                    // as invalid2506                    !matches!(collection.mode, CollectionMode::Fungible(_)) &&2507                    data.len() <= collection.limits.sponsored_data_size as usize2508                {2509                    if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2510                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;25112512                        if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2513                            .map(|last_block| block_number - last_block > rate_limit)2514                            .unwrap_or(true) 2515                        {2516                            sponsor_metadata_changes = true;2517                            <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2518                        }2519                    }2520                }25212522                if !sponsor_metadata_changes {2523                    T::AccountId::default()2524                } else {2525                    collection.sponsor2526                }2527            }25282529            _ => T::AccountId::default(),2530        };25312532        // Sponsor smart contracts2533        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {25342535            // On instantiation: set the contract owner2536            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {25372538                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2539                    &who,2540                    code_hash,2541                    salt,2542                );2543                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());25442545                T::AccountId::default()2546            },25472548            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2549            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {25502551                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());25522553                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2554                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2555                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2556                  2557                if !owned_contract && white_list_enabled {2558                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2559                        return Err(InvalidTransaction::Call.into());2560                    }2561                }25622563                let mut sponsor_transfer = false;2564                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2565                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2566                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2567                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2568                    let limit_time = last_tx_block + rate_limit;25692570                    if block_number >= limit_time {2571                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2572                        sponsor_transfer = true;2573                    }2574                } else {2575                    sponsor_transfer = false;2576                }2577               2578                2579                let mut sp = T::AccountId::default();2580                if sponsor_transfer {2581                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2582                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2583                            sp = called_contract;2584                        }2585                    }2586                }25872588                sp2589            },25902591            _ => sponsor,2592        };25932594        let mut who_pays_fee: T::AccountId = sponsor.clone();2595        if sponsor == T::AccountId::default() {2596            who_pays_fee = who.clone();2597        }25982599		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2600			.map(|i| (fee, i))2601    }2602}260326042605impl<T: Config + Send + Sync> SignedExtension2606    for ChargeTransactionPayment<T>2607where2608    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2609    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2610    T::AccountId: AsRef<[u8]>,2611    T::AccountId: UncheckedFrom<T::Hash>,2612{2613    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2614    type AccountId = T::AccountId;2615    type Call = T::Call;2616    type AdditionalSigned = ();2617    type Pre = (2618        // tip2619        BalanceOf<T>,2620        // who pays fee2621        Self::AccountId,2622		// imbalance resulting from withdrawing the fee2623		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2624    );2625    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2626        Ok(())2627    }26282629    fn validate(2630        &self,2631        who: &Self::AccountId,2632        call: &Self::Call,2633        info: &DispatchInfoOf<Self::Call>,2634        len: usize,2635    ) -> TransactionValidity {2636		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2637		Ok(ValidTransaction {2638			priority: Self::get_priority(len, info, fee),2639			..Default::default()2640		})2641    }26422643    fn pre_dispatch(2644        self,2645        who: &Self::AccountId,2646        call: &Self::Call,2647        info: &DispatchInfoOf<Self::Call>,2648        len: usize,2649    ) -> Result<Self::Pre, TransactionValidityError> {2650        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2651        Ok((self.0, who.clone(), imbalance))2652    }26532654    fn post_dispatch(2655        pre: Self::Pre,2656        info: &DispatchInfoOf<Self::Call>,2657        post_info: &PostDispatchInfoOf<Self::Call>,2658        len: usize,2659        _result: &DispatchResult,2660    ) -> Result<(), TransactionValidityError> {2661		let (tip, who, imbalance) = pre;2662		let actual_fee = transaction_payment::Module::<T>::compute_actual_fee(2663			len as u32,2664			info,2665			post_info,2666			tip,2667		);2668		<T as transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2669		Ok(())2670    }2671}26722673// #endregion