git.delta.rocks / unique-network / refs/commits / 31c0a8fb6eb8

difftreelog

Fix NFTPAR-291 - approval of non-existing token

Greg Zaitsev2021-01-20parent: #517dd9f.patch.diff
in: master

2 files 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, WithdrawReason,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial,29    },30    IsSubType, 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 pallet_contracts::ContractAddressFor;45use sp_runtime::traits::StaticLookup;4647#[cfg(test)]48mod mock;4950#[cfg(test)]51mod tests;5253mod default_weights;5455pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;56pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;57pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;5859// Structs60// #region6162pub type CollectionId = u32;63pub type TokenId = u32;64pub type DecimalPoints = u8;6566#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]67#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]68pub enum CollectionMode {69    Invalid,70    NFT,71    // decimal points72    Fungible(DecimalPoints),73    // decimal points74    ReFungible(DecimalPoints),75}7677impl Default for CollectionMode {78    fn default() -> Self {79        Self::Invalid80    }81}8283impl Into<u8> for CollectionMode {84    fn into(self) -> u8 {85        match self {86            CollectionMode::Invalid => 0,87            CollectionMode::NFT => 1,88            CollectionMode::Fungible(_) => 2,89            CollectionMode::ReFungible(_) => 3,90        }91    }92}9394#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]95#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]96pub enum AccessMode {97    Normal,98    WhiteList,99}100impl Default for AccessMode {101    fn default() -> Self {102        Self::Normal103    }104}105106#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]107#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]108pub enum SchemaVersion {109    ImageURL,110    Unique,111}112impl Default for SchemaVersion {113    fn default() -> Self {114        Self::ImageURL115    }116}117118#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]119#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]120pub struct Ownership<AccountId> {121    pub owner: AccountId,122    pub fraction: u128,123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct CollectionType<AccountId> {128    pub owner: AccountId,129    pub mode: CollectionMode,130    pub access: AccessMode,131    pub decimal_points: DecimalPoints,132    pub name: Vec<u16>,        // 64 include null escape char133    pub description: Vec<u16>, // 256 include null escape char134    pub token_prefix: Vec<u8>, // 16 include null escape char135    pub mint_mode: bool,136    pub offchain_schema: Vec<u8>,137    pub schema_version: SchemaVersion,138    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender139    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.140    pub limits: CollectionLimits, // Collection private restrictions 141    pub variable_on_chain_schema: Vec<u8>, //142    pub const_on_chain_schema: Vec<u8>, //143}144145#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]146#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]147pub struct NftItemType<AccountId> {148    pub owner: AccountId,149    pub const_data: Vec<u8>,150    pub variable_data: Vec<u8>,151}152153#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]154#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]155pub struct FungibleItemType {156    pub value: u128,157}158159#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]160#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]161pub struct ReFungibleItemType<AccountId> {162    pub owner: Vec<Ownership<AccountId>>,163    pub const_data: Vec<u8>,164    pub variable_data: Vec<u8>,165}166167// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]168// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169// pub struct VestingItem<AccountId, Moment> {170//     pub sender: AccountId,171//     pub recipient: AccountId,172//     pub collection_id: CollectionId,173//     pub item_id: TokenId,174//     pub amount: u64,175//     pub vesting_date: Moment,176// }177178#[derive(Encode, Decode, Debug, Clone, PartialEq)]179#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]180pub struct CollectionLimits {181    pub account_token_ownership_limit: u32,182    pub sponsored_data_size: u32,183    pub token_limit: u32,184185    // Timeouts for item types in passed blocks186    pub sponsor_transfer_timeout: u32,187}188189impl Default for CollectionLimits {190    fn default() -> CollectionLimits {191        CollectionLimits { 192            account_token_ownership_limit: 10_000_000, 193            token_limit: u32::max_value(),194            sponsored_data_size: u32::max_value(), 195            sponsor_transfer_timeout: 14400 }196    }197}198199#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]200#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]201pub struct ChainLimits {202    pub collection_numbers_limit: u32,203    pub account_token_ownership_limit: u32,204    pub collections_admins_limit: u64,205    pub custom_data_limit: u32,206207    // Timeouts for item types in passed blocks208    pub nft_sponsor_transfer_timeout: u32,209    pub fungible_sponsor_transfer_timeout: u32,210    pub refungible_sponsor_transfer_timeout: u32,211212    // Schema limits213    pub offchain_schema_limit: u32,214    pub variable_on_chain_schema_limit: u32,215    pub const_on_chain_schema_limit: u32,216}217218pub trait WeightInfo {219	fn create_collection() -> Weight;220	fn destroy_collection() -> Weight;221	fn add_to_white_list() -> Weight;222	fn remove_from_white_list() -> Weight;223    fn set_public_access_mode() -> Weight;224    fn set_mint_permission() -> Weight;225    fn change_collection_owner() -> Weight;226    fn add_collection_admin() -> Weight;227    fn remove_collection_admin() -> Weight;228    fn set_collection_sponsor() -> Weight;229    fn confirm_sponsorship() -> Weight;230    fn remove_collection_sponsor() -> Weight;231    fn create_item(s: usize) -> Weight;232    fn burn_item() -> Weight;233    fn transfer() -> Weight;234    fn approve() -> Weight;235    fn transfer_from() -> Weight;236    fn set_offchain_schema() -> Weight;237    fn set_const_on_chain_schema() -> Weight;238    fn set_variable_on_chain_schema() -> Weight;239    fn set_variable_meta_data() -> Weight;240    fn enable_contract_sponsoring() -> Weight;241    fn set_schema_version() -> Weight;242    fn set_chain_limits() -> Weight;243    fn set_contract_sponsoring_rate_limit() -> Weight;244    fn toggle_contract_white_list() -> Weight;245    fn add_to_contract_white_list() -> Weight;246    fn remove_from_contract_white_list() -> Weight;247    fn set_collection_limits() -> Weight;248}249250#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]251#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]252pub struct CreateNftData {253    pub const_data: Vec<u8>,254    pub variable_data: Vec<u8>,255}256257#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]258#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]259pub struct CreateFungibleData {260    pub value: u128,261}262263#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]264#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]265pub struct CreateReFungibleData {266    pub const_data: Vec<u8>,267    pub variable_data: Vec<u8>,268}269270#[derive(Encode, Decode, Debug, Clone, PartialEq)]271#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]272pub enum CreateItemData {273    NFT(CreateNftData),274    Fungible(CreateFungibleData),275    ReFungible(CreateReFungibleData),276}277278impl CreateItemData {279    pub fn len(&self) -> usize {280        let len = match self {281            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),282            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),283            _ => 0284        };285        286        return len;287    }288}289290impl From<CreateNftData> for CreateItemData {291    fn from(item: CreateNftData) -> Self {292        CreateItemData::NFT(item)293    }294}295296impl From<CreateReFungibleData> for CreateItemData {297    fn from(item: CreateReFungibleData) -> Self {298        CreateItemData::ReFungible(item)299    }300}301302impl From<CreateFungibleData> for CreateItemData {303    fn from(item: CreateFungibleData) -> Self {304        CreateItemData::Fungible(item)305    }306}307308309decl_error! {310	/// Error for non-fungible-token module.311	pub enum Error for Module<T: Trait> {312        /// Total collections bound exceeded.313        TotalCollectionsLimitExceeded,314		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.315        CollectionDecimalPointLimitExceeded, 316        /// Collection name can not be longer than 63 char.317        CollectionNameLimitExceeded, 318        /// Collection description can not be longer than 255 char.319        CollectionDescriptionLimitExceeded, 320        /// Token prefix can not be longer than 15 char.321        CollectionTokenPrefixLimitExceeded,322        /// This collection does not exist.323        CollectionNotFound,324        /// Item not exists.325        TokenNotFound,326        /// Arithmetic calculation overflow.327        NumOverflow,       328        /// Account already has admin role.329        AlreadyAdmin,  330        /// You do not own this collection.331        NoPermission,332        /// This address is not set as sponsor, use setCollectionSponsor first.333        ConfirmUnsetSponsorFail,334        /// Collection is not in mint mode.335        PublicMintingNotAllowed,336        /// Sender parameter and item owner must be equal.337        MustBeTokenOwner,338        /// Item balance not enough.339        TokenValueTooLow,340        /// Size of item is too large.341        NftSizeLimitExceeded,342        /// No approve found343        ApproveNotFound,344        /// Requested value more than approved.345        TokenValueNotEnough,346        /// Only approved addresses can call this method.347        ApproveRequired,348        /// Address is not in white list.349        AddresNotInWhiteList,350        /// Number of collection admins bound exceeded.351        CollectionAdminsLimitExceeded,352        /// Owned tokens by a single address bound exceeded.353        AddressOwnershipLimitExceeded,354        /// Length of items properties must be greater than 0.355        EmptyArgument,356        /// const_data exceeded data limit.357        TokenConstDataLimitExceeded,358        /// variable_data exceeded data limit.359        TokenVariableDataLimitExceeded,360        /// Not NFT item data used to mint in NFT collection.361        NotNftDataUsedToMintNftCollectionToken,362        /// Not Fungible item data used to mint in Fungible collection.363        NotFungibleDataUsedToMintFungibleCollectionToken,364        /// Not Re Fungible item data used to mint in Re Fungible collection.365        NotReFungibleDataUsedToMintReFungibleCollectionToken,366        /// Unexpected collection type.367        UnexpectedCollectionType,368        /// Can't store metadata in fungible tokens.369        CantStoreMetadataInFungibleTokens,370        /// Collection token limit exceeded371        CollectionTokenLimitExceeded,372        /// Account token limit exceeded per collection373        AccountTokenLimitExceeded,374        /// Collection limit bounds per collection exceeded375        CollectionLimitBoundsExceeded,376        /// Schema data size limit bound exceeded377        SchemaDataLimitExceeded378	}379}380381pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {382    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;383384    /// Weight information for extrinsics in this pallet.385	type WeightInfo: WeightInfo;386}387388#[cfg(feature = "runtime-benchmarks")]389mod benchmarking;390391// #endregion392393decl_storage! {394    trait Store for Module<T: Trait> as Nft {395396        // Private members397        NextCollectionID: CollectionId;398        CreatedCollectionCount: u32;399        ChainVersion: u64;400        ItemListIndex: map hasher(identity) CollectionId => TokenId;401402        // Chain limits struct403        pub ChainLimit get(fn chain_limit) config(): ChainLimits;404405        // Bound counters406        CollectionCount: u32;407        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;408409        // Basic collections410        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;411        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;412        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;413414        /// Balance owner per collection map415        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;416417        /// second parameter: item id + owner account id + spender account id418        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;419420        /// Item collections421        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;422        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;423        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;424425        /// Index list426        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;427428        /// Tokens transfer baskets429        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;430        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;431        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;432        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;433434        // Contract Sponsorship and Ownership435        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;436        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;437        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;438        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;439        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 440        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 441    }442    add_extra_genesis {443        build(|config: &GenesisConfig<T>| {444            // Modification of storage445            for (_num, _c) in &config.collection {446                <Module<T>>::init_collection(_c);447            }448449            for (_num, _c, _i) in &config.nft_item_id {450                <Module<T>>::init_nft_token(*_c, _i);451            }452453            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {454                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);455            }456457            for (_num, _c, _i) in &config.refungible_item_id {458                <Module<T>>::init_refungible_token(*_c, _i);459            }460        })461    }462}463464decl_event!(465    pub enum Event<T>466    where467        AccountId = <T as system::Trait>::AccountId,468    {469        /// New collection was created470        /// 471        /// # Arguments472        /// 473        /// * collection_id: Globally unique identifier of newly created collection.474        /// 475        /// * mode: [CollectionMode] converted into u8.476        /// 477        /// * account_id: Collection owner.478        Created(CollectionId, u8, AccountId),479480        /// New item was created.481        /// 482        /// # Arguments483        /// 484        /// * collection_id: Id of the collection where item was created.485        /// 486        /// * item_id: Id of an item. Unique within the collection.487        ItemCreated(CollectionId, TokenId),488489        /// Collection item was burned.490        /// 491        /// # Arguments492        /// 493        /// collection_id.494        /// 495        /// item_id: Identifier of burned NFT.496        ItemDestroyed(CollectionId, TokenId),497    }498);499500decl_module! {501    pub struct Module<T: Trait> for enum Call where origin: T::Origin {502503        fn deposit_event() = default;504        type Error = Error<T>;505506        fn on_initialize(now: T::BlockNumber) -> Weight {507508            if ChainVersion::get() < 2509            {510                let value = NextCollectionID::get();511                CreatedCollectionCount::put(value);512                ChainVersion::put(2);513            }514515            0516        }517518        /// 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.519        /// 520        /// # Permissions521        /// 522        /// * Anyone.523        /// 524        /// # Arguments525        /// 526        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.527        /// 528        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.529        /// 530        /// * token_prefix: UTF-8 string with token prefix.531        /// 532        /// * mode: [CollectionMode] collection type and type dependent data.533        // returns collection ID534        #[weight = T::WeightInfo::create_collection()]535        pub fn create_collection(origin,536                                 collection_name: Vec<u16>,537                                 collection_description: Vec<u16>,538                                 token_prefix: Vec<u8>,539                                 mode: CollectionMode) -> DispatchResult {540541            // Anyone can create a collection542            let who = ensure_signed(origin)?;543544            let decimal_points = match mode {545                CollectionMode::Fungible(points) => points,546                CollectionMode::ReFungible(points) => points,547                _ => 0548            };549550            // bound Total number of collections551            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);552553            // check params554            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);555            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);556            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);557            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);558559            // Generate next collection ID560            let next_id = CreatedCollectionCount::get()561                .checked_add(1)562                .ok_or(Error::<T>::NumOverflow)?;563564            // bound counter565            let total = CollectionCount::get()566                .checked_add(1)567                .ok_or(Error::<T>::NumOverflow)?;568569            CreatedCollectionCount::put(next_id);570            CollectionCount::put(total);571572            // Create new collection573            let new_collection = CollectionType {574                owner: who.clone(),575                name: collection_name,576                mode: mode.clone(),577                mint_mode: false,578                access: AccessMode::Normal,579                description: collection_description,580                decimal_points: decimal_points,581                token_prefix: token_prefix,582                offchain_schema: Vec::new(),583                schema_version: SchemaVersion::ImageURL,584                sponsor: T::AccountId::default(),585                sponsor_confirmed: false,586                variable_on_chain_schema: Vec::new(),587                const_on_chain_schema: Vec::new(),588                limits: CollectionLimits::default(),589            };590591            // Add new collection to map592            <Collection<T>>::insert(next_id, new_collection);593594            // call event595            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));596597            Ok(())598        }599600        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.601        /// 602        /// # Permissions603        /// 604        /// * Collection Owner.605        /// 606        /// # Arguments607        /// 608        /// * collection_id: collection to destroy.609        #[weight = T::WeightInfo::destroy_collection()]610        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {611612            let sender = ensure_signed(origin)?;613            Self::check_owner_permissions(collection_id, sender)?;614615            <AddressTokens<T>>::remove_prefix(collection_id);616            <Allowances<T>>::remove_prefix(collection_id);617            <Balance<T>>::remove_prefix(collection_id);618            <ItemListIndex>::remove(collection_id);619            <AdminList<T>>::remove(collection_id);620            <Collection<T>>::remove(collection_id);621            <WhiteList<T>>::remove_prefix(collection_id);622623            <NftItemList<T>>::remove_prefix(collection_id);624            <FungibleItemList<T>>::remove_prefix(collection_id);625            <ReFungibleItemList<T>>::remove_prefix(collection_id);626627            <NftTransferBasket<T>>::remove_prefix(collection_id);628            <FungibleTransferBasket<T>>::remove_prefix(collection_id);629            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);630631            if CollectionCount::get() > 0632            {633                // bound couter634                let total = CollectionCount::get()635                    .checked_sub(1)636                    .ok_or(Error::<T>::NumOverflow)?;637638                CollectionCount::put(total);639            }640641            Ok(())642        }643644        /// Add an address to white list.645        /// 646        /// # Permissions647        /// 648        /// * Collection Owner649        /// * Collection Admin650        /// 651        /// # Arguments652        /// 653        /// * collection_id.654        /// 655        /// * address.656        #[weight = T::WeightInfo::add_to_white_list()]657        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{658659            let sender = ensure_signed(origin)?;660            Self::check_owner_or_admin_permissions(collection_id, sender)?;661662            <WhiteList<T>>::insert(collection_id, address, true);663            664            Ok(())665        }666667        /// Remove an address from white list.668        /// 669        /// # Permissions670        /// 671        /// * Collection Owner672        /// * Collection Admin673        /// 674        /// # Arguments675        /// 676        /// * collection_id.677        /// 678        /// * address.679        #[weight = T::WeightInfo::remove_from_white_list()]680        pub fn remove_from_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>>::remove(collection_id, address);686687            Ok(())688        }689690        /// Toggle between normal and white list access for the methods with access for `Anyone`.691        /// 692        /// # Permissions693        /// 694        /// * Collection Owner.695        /// 696        /// # Arguments697        /// 698        /// * collection_id.699        /// 700        /// * mode: [AccessMode]701        #[weight = T::WeightInfo::set_public_access_mode()]702        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult703        {704            let sender = ensure_signed(origin)?;705706            Self::check_owner_permissions(collection_id, sender)?;707            let mut target_collection = <Collection<T>>::get(collection_id);708            target_collection.access = mode;709            <Collection<T>>::insert(collection_id, target_collection);710711            Ok(())712        }713714        /// Allows Anyone to create tokens if:715        /// * White List is enabled, and716        /// * Address is added to white list, and717        /// * This method was called with True parameter718        /// 719        /// # Permissions720        /// * Collection Owner721        ///722        /// # Arguments723        /// 724        /// * collection_id.725        /// 726        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.727        #[weight = T::WeightInfo::set_mint_permission()]728        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult729        {730            let sender = ensure_signed(origin)?;731732            Self::check_owner_permissions(collection_id, sender)?;733            let mut target_collection = <Collection<T>>::get(collection_id);734            target_collection.mint_mode = mint_permission;735            <Collection<T>>::insert(collection_id, target_collection);736737            Ok(())738        }739740        /// Change the owner of the collection.741        /// 742        /// # Permissions743        /// 744        /// * Collection Owner.745        /// 746        /// # Arguments747        /// 748        /// * collection_id.749        /// 750        /// * new_owner.751        #[weight = T::WeightInfo::change_collection_owner()]752        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {753754            let sender = ensure_signed(origin)?;755            Self::check_owner_permissions(collection_id, sender)?;756            let mut target_collection = <Collection<T>>::get(collection_id);757            target_collection.owner = new_owner;758            <Collection<T>>::insert(collection_id, target_collection);759760            Ok(())761        }762763        /// Adds an admin of the Collection.764        /// 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. 765        /// 766        /// # Permissions767        /// 768        /// * Collection Owner.769        /// * Collection Admin.770        /// 771        /// # Arguments772        /// 773        /// * collection_id: ID of the Collection to add admin for.774        /// 775        /// * new_admin_id: Address of new admin to add.776        #[weight = T::WeightInfo::add_collection_admin()]777        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {778779            let sender = ensure_signed(origin)?;780            Self::check_owner_or_admin_permissions(collection_id, sender)?;781            let mut admin_arr: Vec<T::AccountId> = Vec::new();782783            if <AdminList<T>>::contains_key(collection_id)784            {785                admin_arr = <AdminList<T>>::get(collection_id);786                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);787            }788789            // Number of collection admins790            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);791792            admin_arr.push(new_admin_id);793            <AdminList<T>>::insert(collection_id, admin_arr);794795            Ok(())796        }797798        /// 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.799        ///800        /// # Permissions801        /// 802        /// * Collection Owner.803        /// * Collection Admin.804        /// 805        /// # Arguments806        /// 807        /// * collection_id: ID of the Collection to remove admin for.808        /// 809        /// * account_id: Address of admin to remove.810        #[weight = T::WeightInfo::remove_collection_admin()]811        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {812813            let sender = ensure_signed(origin)?;814            Self::check_owner_or_admin_permissions(collection_id, sender)?;815816            if <AdminList<T>>::contains_key(collection_id)817            {818                let mut admin_arr = <AdminList<T>>::get(collection_id);819                admin_arr.retain(|i| *i != account_id);820                <AdminList<T>>::insert(collection_id, admin_arr);821            }822823            Ok(())824        }825826        /// # Permissions827        /// 828        /// * Collection Owner829        /// 830        /// # Arguments831        /// 832        /// * collection_id.833        /// 834        /// * new_sponsor.835        #[weight = T::WeightInfo::set_collection_sponsor()]836        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {837838            let sender = ensure_signed(origin)?;839            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);840841            let mut target_collection = <Collection<T>>::get(collection_id);842            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);843844            target_collection.sponsor = new_sponsor;845            target_collection.sponsor_confirmed = false;846            <Collection<T>>::insert(collection_id, target_collection);847848            Ok(())849        }850851        /// # Permissions852        /// 853        /// * Sponsor.854        /// 855        /// # Arguments856        /// 857        /// * collection_id.858        #[weight = T::WeightInfo::confirm_sponsorship()]859        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {860861            let sender = ensure_signed(origin)?;862            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);863864            let mut target_collection = <Collection<T>>::get(collection_id);865            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);866867            target_collection.sponsor_confirmed = true;868            <Collection<T>>::insert(collection_id, target_collection);869870            Ok(())871        }872873        /// Switch back to pay-per-own-transaction model.874        ///875        /// # Permissions876        ///877        /// * Collection owner.878        /// 879        /// # Arguments880        /// 881        /// * collection_id.882        #[weight = T::WeightInfo::remove_collection_sponsor()]883        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {884885            let sender = ensure_signed(origin)?;886            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);887888            let mut target_collection = <Collection<T>>::get(collection_id);889            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);890891            target_collection.sponsor = T::AccountId::default();892            target_collection.sponsor_confirmed = false;893            <Collection<T>>::insert(collection_id, target_collection);894895            Ok(())896        }897898        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.899        /// 900        /// # Permissions901        /// 902        /// * Collection Owner.903        /// * Collection Admin.904        /// * Anyone if905        ///     * White List is enabled, and906        ///     * Address is added to white list, and907        ///     * MintPermission is enabled (see SetMintPermission method)908        /// 909        /// # Arguments910        /// 911        /// * collection_id: ID of the collection.912        /// 913        /// * owner: Address, initial owner of the NFT.914        ///915        /// * data: Token data to store on chain.916        // #[weight =917        // (130_000_000 as Weight)918        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))919        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))920        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]921922        #[weight = T::WeightInfo::create_item(data.len())]923        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {924925            let sender = ensure_signed(origin)?;926927            Self::collection_exists(collection_id)?;928929            let target_collection = <Collection<T>>::get(collection_id);930931            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;932            Self::validate_create_item_args(&target_collection, &data)?;933            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;934935            Ok(())936        }937938        /// This method creates multiple instances of NFT Collection created with CreateCollection method.939        /// 940        /// # Permissions941        /// 942        /// * Collection Owner.943        /// * Collection Admin.944        /// * Anyone if945        ///     * White List is enabled, and946        ///     * Address is added to white list, and947        ///     * MintPermission is enabled (see SetMintPermission method)948        /// 949        /// # Arguments950        /// 951        /// * collection_id: ID of the collection.952        /// 953        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].954        /// 955        /// * owner: Address, initial owner of the NFT.956        #[weight = T::WeightInfo::create_item(items_data.into_iter()957                               .map(|data| { data.len() })958                               .sum())]959        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {960961            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);962            let sender = ensure_signed(origin)?;963964            Self::collection_exists(collection_id)?;965            let target_collection = <Collection<T>>::get(collection_id);966967            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;968969            for data in &items_data {970                Self::validate_create_item_args(&target_collection, data)?;971            }972            for data in &items_data {973                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;974            }975976            Ok(())977        }978979        /// Destroys a concrete instance of NFT.980        /// 981        /// # Permissions982        /// 983        /// * Collection Owner.984        /// * Collection Admin.985        /// * Current NFT Owner.986        /// 987        /// # Arguments988        /// 989        /// * collection_id: ID of the collection.990        /// 991        /// * item_id: ID of NFT to burn.992        #[weight = T::WeightInfo::burn_item()]993        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {994995            let sender = ensure_signed(origin)?;996            Self::collection_exists(collection_id)?;997998            // Transfer permissions check999            let target_collection = <Collection<T>>::get(collection_id);1000            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1001                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1002                Error::<T>::NoPermission);10031004            if target_collection.access == AccessMode::WhiteList {1005                Self::check_white_list(collection_id, &sender)?;1006            }10071008            match target_collection.mode1009            {1010                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1011                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1012                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1013                _ => ()1014            };10151016            // call event1017            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10181019            Ok(())1020        }10211022        /// Change ownership of the token.1023        /// 1024        /// # Permissions1025        /// 1026        /// * Collection Owner1027        /// * Collection Admin1028        /// * Current NFT owner1029        ///1030        /// # Arguments1031        /// 1032        /// * recipient: Address of token recipient.1033        /// 1034        /// * collection_id.1035        /// 1036        /// * item_id: ID of the item1037        ///     * Non-Fungible Mode: Required.1038        ///     * Fungible Mode: Ignored.1039        ///     * Re-Fungible Mode: Required.1040        /// 1041        /// * value: Amount to transfer.1042        ///     * Non-Fungible Mode: Ignored1043        ///     * Fungible Mode: Must specify transferred amount1044        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1045        #[weight = T::WeightInfo::transfer()]1046        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10471048            let sender = ensure_signed(origin)?;1049            let target_collection = <Collection<T>>::get(collection_id);10501051            // Limits check1052            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;10531054            // Transfer permissions check1055            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1056                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1057                Error::<T>::NoPermission);10581059            if target_collection.access == AccessMode::WhiteList {1060                Self::check_white_list(collection_id, &sender)?;1061                Self::check_white_list(collection_id, &recipient)?;1062            }10631064            match target_collection.mode1065            {1066                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1067                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1068                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1069                _ => ()1070            };10711072            Ok(())1073        }10741075        /// Set, change, or remove approved address to transfer the ownership of the NFT.1076        /// 1077        /// # Permissions1078        /// 1079        /// * Collection Owner1080        /// * Collection Admin1081        /// * Current NFT owner1082        /// 1083        /// # Arguments1084        /// 1085        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1086        /// 1087        /// * collection_id.1088        /// 1089        /// * item_id: ID of the item.1090        #[weight = T::WeightInfo::approve()]1091        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {10921093            let sender = ensure_signed(origin)?;10941095            // Transfer permissions check1096            let target_collection = <Collection<T>>::get(collection_id);1097            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1098                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1099                Error::<T>::NoPermission);11001101            if target_collection.access == AccessMode::WhiteList {1102                Self::check_white_list(collection_id, &sender)?;1103                Self::check_white_list(collection_id, &spender)?;1104            }11051106            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1107            let mut allowance: u128 = amount;1108            if allowance_exists {1109                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1110            }1111            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11121113            Ok(())1114        }1115        1116        /// 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.1117        /// 1118        /// # Permissions1119        /// * Collection Owner1120        /// * Collection Admin1121        /// * Current NFT owner1122        /// * Address approved by current NFT owner1123        /// 1124        /// # Arguments1125        /// 1126        /// * from: Address that owns token.1127        /// 1128        /// * recipient: Address of token recipient.1129        /// 1130        /// * collection_id.1131        /// 1132        /// * item_id: ID of the item.1133        /// 1134        /// * value: Amount to transfer.1135        #[weight = T::WeightInfo::transfer_from()]1136        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11371138            let sender = ensure_signed(origin)?;1139            let mut appoved_transfer = false;11401141            // Check approval1142            let mut approval: u128 = 0;1143            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &recipient)) {1144                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &recipient));1145                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1146                appoved_transfer = true;1147            }11481149            let target_collection = <Collection<T>>::get(collection_id);11501151            // Limits check1152            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11531154            // Transfer permissions check         1155            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1156                Error::<T>::NoPermission);11571158            if target_collection.access == AccessMode::WhiteList {1159                Self::check_white_list(collection_id, &sender)?;1160                Self::check_white_list(collection_id, &recipient)?;1161            }11621163            // Reduce approval by transferred amount or remove if remaining approval drops to 01164            if approval.checked_sub(value).unwrap_or(0) > 0 {1165                <Allowances<T>>::insert(collection_id, (item_id, &from, &recipient), approval - value);1166            }1167            else {1168                <Allowances<T>>::remove(collection_id, (item_id, &from, &recipient));1169            }11701171            match target_collection.mode1172            {1173                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1174                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1175                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1176                _ => ()1177            };11781179            Ok(())1180        }11811182        #[weight = 0]1183        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11841185            // let no_perm_mes = "You do not have permissions to modify this collection";1186            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1187            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1188            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11891190            // // on_nft_received  call11911192            // Self::transfer(origin, collection_id, item_id, new_owner)?;11931194            Ok(())1195        }11961197        /// Set off-chain data schema.1198        /// 1199        /// # Permissions1200        /// 1201        /// * Collection Owner1202        /// * Collection Admin1203        /// 1204        /// # Arguments1205        /// 1206        /// * collection_id.1207        /// 1208        /// * schema: String representing the offchain data schema.1209        #[weight = T::WeightInfo::set_variable_meta_data()]1210        pub fn set_variable_meta_data (1211            origin,1212            collection_id: CollectionId,1213            item_id: TokenId,1214            data: Vec<u8>1215        ) -> DispatchResult {1216            let sender = ensure_signed(origin)?;1217            1218            Self::collection_exists(collection_id)?;1219            1220            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12211222            // Modify permissions check1223            let target_collection = <Collection<T>>::get(collection_id);1224            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1225                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1226                Error::<T>::NoPermission);12271228            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12291230            match target_collection.mode1231            {1232                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1233                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1234                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1235                _ => fail!(Error::<T>::UnexpectedCollectionType)1236            };12371238            Ok(())1239        }1240 1241        /// Set schema standard1242        /// ImageURL1243        /// Unique1244        /// 1245        /// # Permissions1246        /// 1247        /// * Collection Owner1248        /// * Collection Admin1249        /// 1250        /// # Arguments1251        /// 1252        /// * collection_id.1253        /// 1254        /// * schema: SchemaVersion: enum1255        #[weight = T::WeightInfo::set_schema_version()]1256        pub fn set_schema_version(1257            origin,1258            collection_id: CollectionId,1259            version: SchemaVersion1260        ) -> DispatchResult {1261            let sender = ensure_signed(origin)?;1262            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1263            let mut target_collection = <Collection<T>>::get(collection_id);1264            target_collection.schema_version = version;1265            <Collection<T>>::insert(collection_id, target_collection);12661267            Ok(())1268        }12691270        /// Set off-chain data schema.1271        /// 1272        /// # Permissions1273        /// 1274        /// * Collection Owner1275        /// * Collection Admin1276        /// 1277        /// # Arguments1278        /// 1279        /// * collection_id.1280        /// 1281        /// * schema: String representing the offchain data schema.1282        #[weight = T::WeightInfo::set_offchain_schema()]1283        pub fn set_offchain_schema(1284            origin,1285            collection_id: CollectionId,1286            schema: Vec<u8>1287        ) -> DispatchResult {1288            let sender = ensure_signed(origin)?;1289            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12901291            // check schema limit1292            ensure!(schema.len() as u32 > ChainLimit::get().offchain_schema_limit, "");12931294            let mut target_collection = <Collection<T>>::get(collection_id);1295            target_collection.offchain_schema = schema;1296            <Collection<T>>::insert(collection_id, target_collection);12971298            Ok(())1299        }13001301        /// Set const on-chain data schema.1302        /// 1303        /// # Permissions1304        /// 1305        /// * Collection Owner1306        /// * Collection Admin1307        /// 1308        /// # Arguments1309        /// 1310        /// * collection_id.1311        /// 1312        /// * schema: String representing the const on-chain data schema.1313        #[weight = T::WeightInfo::set_const_on_chain_schema()]1314        pub fn set_const_on_chain_schema (1315            origin,1316            collection_id: CollectionId,1317            schema: Vec<u8>1318        ) -> DispatchResult {1319            let sender = ensure_signed(origin)?;1320            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13211322            // check schema limit1323            ensure!(schema.len() as u32 > ChainLimit::get().const_on_chain_schema_limit, "");13241325            let mut target_collection = <Collection<T>>::get(collection_id);1326            target_collection.const_on_chain_schema = schema;1327            <Collection<T>>::insert(collection_id, target_collection);13281329            Ok(())1330        }13311332        /// Set variable on-chain data schema.1333        /// 1334        /// # Permissions1335        /// 1336        /// * Collection Owner1337        /// * Collection Admin1338        /// 1339        /// # Arguments1340        /// 1341        /// * collection_id.1342        /// 1343        /// * schema: String representing the variable on-chain data schema.1344        #[weight = T::WeightInfo::set_const_on_chain_schema()]1345        pub fn set_variable_on_chain_schema (1346            origin,1347            collection_id: CollectionId,1348            schema: Vec<u8>1349        ) -> DispatchResult {1350            let sender = ensure_signed(origin)?;1351            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13521353            // check schema limit1354            ensure!(schema.len() as u32 > ChainLimit::get().variable_on_chain_schema_limit, "");13551356            let mut target_collection = <Collection<T>>::get(collection_id);1357            target_collection.variable_on_chain_schema = schema;1358            <Collection<T>>::insert(collection_id, target_collection);13591360            Ok(())1361        }13621363        // Sudo permissions function1364        #[weight = T::WeightInfo::set_chain_limits()]1365        pub fn set_chain_limits(1366            origin,1367            limits: ChainLimits1368        ) -> DispatchResult {13691370            #[cfg(not(feature = "runtime-benchmarks"))]1371            ensure_root(origin)?;13721373            <ChainLimit>::put(limits);1374            Ok(())1375        }13761377        /// Enable smart contract self-sponsoring.1378        /// 1379        /// # Permissions1380        /// 1381        /// * Contract Owner1382        /// 1383        /// # Arguments1384        /// 1385        /// * contract address1386        /// * enable flag1387        /// 1388        #[weight = T::WeightInfo::enable_contract_sponsoring()]1389        pub fn enable_contract_sponsoring(1390            origin,1391            contract_address: T::AccountId,1392            enable: bool1393        ) -> DispatchResult {13941395            let sender = ensure_signed(origin)?;13961397            #[cfg(feature = "runtime-benchmarks")]1398            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13991400            Self::ensure_contract_owned(sender, &contract_address)?;14011402            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1403            Ok(())1404        }14051406        /// Set the rate limit for contract sponsoring to specified number of blocks.1407        /// 1408        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1409        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1410        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1411        /// from contract endowment if there are at least B blocks between such transactions. 1412        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1413        /// 1414        /// # Permissions1415        /// 1416        /// * Contract Owner1417        /// 1418        /// # Arguments1419        /// 1420        /// -`contract_address`: Address of the contract to sponsor1421        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1422        /// 1423        #[weight = T::WeightInfo::set_contract_sponsoring_rate_limit()]1424        pub fn set_contract_sponsoring_rate_limit(1425            origin,1426            contract_address: T::AccountId,1427            rate_limit: T::BlockNumber1428        ) -> DispatchResult {1429            let sender = ensure_signed(origin)?;14301431            #[cfg(feature = "runtime-benchmarks")]1432            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14331434            Self::ensure_contract_owned(sender, &contract_address)?;1435            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1436            Ok(())1437        }14381439        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1440        /// 1441        /// # Permissions1442        /// 1443        /// * Address that deployed smart contract.1444        /// 1445        /// # Arguments1446        /// 1447        /// -`contract_address`: Address of the contract.1448        /// 1449        /// - `enable`: .  1450        #[weight = T::WeightInfo::toggle_contract_white_list()]1451        pub fn toggle_contract_white_list(1452            origin,1453            contract_address: T::AccountId,1454            enable: bool1455        ) -> DispatchResult {1456            let sender = ensure_signed(origin)?;14571458            #[cfg(feature = "runtime-benchmarks")]1459            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14601461            Self::ensure_contract_owned(sender, &contract_address)?;1462            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1463            Ok(())1464        }1465        1466        /// Add an address to smart contract white list.1467        /// 1468        /// # Permissions1469        /// 1470        /// * Address that deployed smart contract.1471        /// 1472        /// # Arguments1473        /// 1474        /// -`contract_address`: Address of the contract.1475        ///1476        /// -`account_address`: Address to add.1477        #[weight = T::WeightInfo::add_to_contract_white_list()]1478        pub fn add_to_contract_white_list(1479            origin,1480            contract_address: T::AccountId,1481            account_address: T::AccountId1482        ) -> DispatchResult {1483            let sender = ensure_signed(origin)?;14841485            #[cfg(feature = "runtime-benchmarks")]1486            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1487            1488            Self::ensure_contract_owned(sender, &contract_address)?;      1489            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1490            Ok(())1491        }14921493        /// Remove an address from smart contract white list.1494        /// 1495        /// # Permissions1496        /// 1497        /// * Address that deployed smart contract.1498        /// 1499        /// # Arguments1500        /// 1501        /// -`contract_address`: Address of the contract.1502        ///1503        /// -`account_address`: Address to remove.1504        #[weight = T::WeightInfo::remove_from_contract_white_list()]1505        pub fn remove_from_contract_white_list(1506            origin,1507            contract_address: T::AccountId,1508            account_address: T::AccountId1509        ) -> DispatchResult {1510            let sender = ensure_signed(origin)?;15111512            #[cfg(feature = "runtime-benchmarks")]1513            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15141515            Self::ensure_contract_owned(sender, &contract_address)?;1516            <ContractWhiteList<T>>::remove(contract_address, account_address);1517            Ok(())1518        }15191520        #[weight = T::WeightInfo::set_collection_limits()]1521        pub fn set_collection_limits(1522            origin,1523            collection_id: u32,1524            limits: CollectionLimits,1525        ) -> DispatchResult {1526            let sender = ensure_signed(origin)?;1527            Self::check_owner_permissions(collection_id, sender.clone())?;1528            let mut target_collection = <Collection<T>>::get(collection_id);1529            let chain_limits = ChainLimit::get();1530            let climits = target_collection.limits;15311532            // collection bounds1533            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1534                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1535                Error::<T>::CollectionLimitBoundsExceeded);15361537            // token_limit   check  prev1538            ensure!(climits.token_limit > limits.token_limit && 1539                limits.token_limit <= chain_limits.account_token_ownership_limit, 1540                Error::<T>::AccountTokenLimitExceeded);15411542            target_collection.limits = limits;1543            <Collection<T>>::insert(collection_id, target_collection);15441545            Ok(())1546        } 1547    }1548}15491550impl<T: Trait> Module<T> {15511552    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15531554        // check token limit and account token limit1555        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1556        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1557        1558        Ok(())1559    }15601561    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15621563        // check token limit and account token limit1564        let total_items: u32 = ItemListIndex::get(collection_id);1565        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1566        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1567        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15681569        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1570            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1571            Self::check_white_list(collection_id, owner)?;1572            Self::check_white_list(collection_id, sender)?;1573        }15741575        Ok(())1576    }15771578    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1579        match target_collection.mode1580        {1581            CollectionMode::NFT => {1582                if let CreateItemData::NFT(data) = data {1583                    // check sizes1584                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1585                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1586                } else {1587                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1588                }1589            },1590            CollectionMode::Fungible(_) => {1591                if let CreateItemData::Fungible(_) = data {1592                } else {1593                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1594                }1595            },1596            CollectionMode::ReFungible(_) => {1597                if let CreateItemData::ReFungible(data) = data {15981599                    // check sizes1600                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1601                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1602                } else {1603                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1604                }1605            },1606            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1607        };16081609        Ok(())1610    }16111612    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1613        match data1614        {1615            CreateItemData::NFT(data) => {1616                let item = NftItemType {1617                    owner,1618                    const_data: data.const_data,1619                    variable_data: data.variable_data1620                };16211622                Self::add_nft_item(collection_id, item)?;1623            },1624            CreateItemData::Fungible(data) => {1625                Self::add_fungible_item(collection_id, &owner, data.value)?;1626            },1627            CreateItemData::ReFungible(data) => {1628                let mut owner_list = Vec::new();1629                let value = (10 as u128).pow(collection.decimal_points as u32);1630                owner_list.push(Ownership {owner: owner.clone(), fraction: value});16311632                let item = ReFungibleItemType {1633                    owner: owner_list,1634                    const_data: data.const_data,1635                    variable_data: data.variable_data1636                };16371638                Self::add_refungible_item(collection_id, item)?;1639            }1640        };16411642        // call event1643        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16441645        Ok(())1646    }16471648    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {16491650        // Does new owner already have an account?1651        let mut balance: u128 = 0;1652        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1653            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1654        } 16551656        // Mint 1657        let item = FungibleItemType {1658            value: balance + value1659        };1660        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16611662        // Update balance1663        let new_balance = <Balance<T>>::get(collection_id, owner)1664            .checked_add(value)1665            .ok_or(Error::<T>::NumOverflow)?;1666        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);16671668        Ok(())1669    }16701671    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1672        let current_index = <ItemListIndex>::get(collection_id)1673            .checked_add(1)1674            .ok_or(Error::<T>::NumOverflow)?;1675        let itemcopy = item.clone();16761677        let value = item.owner.first().unwrap().fraction;1678        let owner = item.owner.first().unwrap().owner.clone();16791680        Self::add_token_index(collection_id, current_index, owner.clone())?;16811682        <ItemListIndex>::insert(collection_id, current_index);1683        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);16841685        // Update balance1686        let new_balance = <Balance<T>>::get(collection_id, owner.clone())1687            .checked_add(value)1688            .ok_or(Error::<T>::NumOverflow)?;1689        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);16901691        Ok(())1692    }16931694    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1695        let current_index = <ItemListIndex>::get(collection_id)1696            .checked_add(1)1697            .ok_or(Error::<T>::NumOverflow)?;16981699        let item_owner = item.owner.clone();1700        Self::add_token_index(collection_id, current_index, item.owner.clone())?;17011702        <ItemListIndex>::insert(collection_id, current_index);1703        <NftItemList<T>>::insert(collection_id, current_index, item);17041705        // Update balance1706        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1707            .checked_add(1)1708            .ok_or(Error::<T>::NumOverflow)?;1709        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17101711        Ok(())1712    }17131714    fn burn_refungible_item(1715        collection_id: CollectionId,1716        item_id: TokenId,1717        owner: T::AccountId,1718    ) -> DispatchResult {1719        ensure!(1720            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1721            Error::<T>::TokenNotFound1722        );1723        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1724        let item = collection1725            .owner1726            .iter()1727            .filter(|&i| i.owner == owner)1728            .next()1729            .unwrap();1730        Self::remove_token_index(collection_id, item_id, owner.clone())?;17311732        // update balance1733        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1734            .checked_sub(item.fraction)1735            .ok_or(Error::<T>::NumOverflow)?;1736        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17371738        <ReFungibleItemList<T>>::remove(collection_id, item_id);17391740        Ok(())1741    }17421743    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1744        ensure!(1745            <NftItemList<T>>::contains_key(collection_id, item_id),1746            Error::<T>::TokenNotFound1747        );1748        let item = <NftItemList<T>>::get(collection_id, item_id);1749        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17501751        // update balance1752        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1753            .checked_sub(1)1754            .ok_or(Error::<T>::NumOverflow)?;1755        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1756        <NftItemList<T>>::remove(collection_id, item_id);17571758        Ok(())1759    }17601761    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1762        ensure!(1763            <FungibleItemList<T>>::contains_key(collection_id, owner),1764            Error::<T>::TokenNotFound1765        );1766        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1767        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);17681769        // update balance1770        let new_balance = <Balance<T>>::get(collection_id, owner)1771            .checked_sub(value)1772            .ok_or(Error::<T>::NumOverflow)?;1773        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17741775        if balance.value - value > 0 {1776            balance.value -= value;1777            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1778        }1779        else {1780            <FungibleItemList<T>>::remove(collection_id, owner);1781        }17821783        Ok(())1784    }17851786    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1787        ensure!(1788            <Collection<T>>::contains_key(collection_id),1789            Error::<T>::CollectionNotFound1790        );1791        Ok(())1792    }17931794    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1795        Self::collection_exists(collection_id)?;17961797        let target_collection = <Collection<T>>::get(collection_id);1798        ensure!(1799            subject == target_collection.owner,1800            Error::<T>::NoPermission1801        );18021803        Ok(())1804    }18051806    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1807        let target_collection = <Collection<T>>::get(collection_id);1808        let mut result: bool = subject == target_collection.owner;1809        let exists = <AdminList<T>>::contains_key(collection_id);18101811        if !result & exists {1812            if <AdminList<T>>::get(collection_id).contains(&subject) {1813                result = true1814            }1815        }18161817        result1818    }18191820    fn check_owner_or_admin_permissions(1821        collection_id: CollectionId,1822        subject: T::AccountId,1823    ) -> DispatchResult {1824        Self::collection_exists(collection_id)?;1825        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18261827        ensure!(1828            result,1829            Error::<T>::NoPermission1830        );1831        Ok(())1832    }18331834    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1835        let target_collection = <Collection<T>>::get(collection_id);18361837        match target_collection.mode {1838            CollectionMode::NFT => {1839                <NftItemList<T>>::get(collection_id, item_id).owner == subject1840            }1841            CollectionMode::Fungible(_) => {1842                <FungibleItemList<T>>::contains_key(collection_id, &subject)1843            }1844            CollectionMode::ReFungible(_) => {1845                <ReFungibleItemList<T>>::get(collection_id, item_id)1846                    .owner1847                    .iter()1848                    .any(|i| i.owner == subject)1849            }1850            CollectionMode::Invalid => false,1851        }1852    }18531854    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1855        let mes = Error::<T>::AddresNotInWhiteList;1856        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);18571858        Ok(())1859    }18601861    fn transfer_fungible(1862        collection_id: CollectionId,1863        value: u128,1864        owner: &T::AccountId,1865        recipient: &T::AccountId,1866    ) -> DispatchResult {1867        ensure!(1868            <FungibleItemList<T>>::contains_key(collection_id, owner),1869            Error::<T>::TokenNotFound1870        );18711872        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1873        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);18741875        // Send balance to recipient (updates balanceOf of recipient)1876        Self::add_fungible_item(collection_id, recipient, value)?;18771878        // update balanceOf of sender1879        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);18801881        // Reduce or remove sender1882        if balance.value == value {1883            <FungibleItemList<T>>::remove(collection_id, owner);1884        }1885        else {1886            balance.value -= value;1887            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1888        }18891890        Ok(())1891    }18921893    fn transfer_refungible(1894        collection_id: CollectionId,1895        item_id: TokenId,1896        value: u128,1897        owner: T::AccountId,1898        new_owner: T::AccountId,1899    ) -> DispatchResult {1900        ensure!(1901            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1902            Error::<T>::TokenNotFound1903        );19041905        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1906        let item = full_item1907            .owner1908            .iter()1909            .filter(|i| i.owner == owner)1910            .next()1911            .ok_or(Error::<T>::NumOverflow)?;1912        let amount = item.fraction;19131914        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19151916        // update balance1917        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1918            .checked_sub(value)1919            .ok_or(Error::<T>::NumOverflow)?;1920        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19211922        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1923            .checked_add(value)1924            .ok_or(Error::<T>::NumOverflow)?;1925        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19261927        let old_owner = item.owner.clone();1928        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19291930        // transfer1931        if amount == value && !new_owner_has_account {1932            // change owner1933            // new owner do not have account1934            let mut new_full_item = full_item.clone();1935            new_full_item1936                .owner1937                .iter_mut()1938                .find(|i| i.owner == owner)1939                .unwrap()1940                .owner = new_owner.clone();1941            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19421943            // update index collection1944            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1945        } else {1946            let mut new_full_item = full_item.clone();1947            new_full_item1948                .owner1949                .iter_mut()1950                .find(|i| i.owner == owner)1951                .unwrap()1952                .fraction -= value;19531954            // separate amount1955            if new_owner_has_account {1956                // new owner has account1957                new_full_item1958                    .owner1959                    .iter_mut()1960                    .find(|i| i.owner == new_owner)1961                    .unwrap()1962                    .fraction += value;1963            } else {1964                // new owner do not have account1965                new_full_item.owner.push(Ownership {1966                    owner: new_owner.clone(),1967                    fraction: value,1968                });1969                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1970            }19711972            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1973        }19741975        Ok(())1976    }19771978    fn transfer_nft(1979        collection_id: CollectionId,1980        item_id: TokenId,1981        sender: T::AccountId,1982        new_owner: T::AccountId,1983    ) -> DispatchResult {1984        ensure!(1985            <NftItemList<T>>::contains_key(collection_id, item_id),1986            Error::<T>::TokenNotFound1987        );19881989        let mut item = <NftItemList<T>>::get(collection_id, item_id);19901991        ensure!(1992            sender == item.owner,1993            Error::<T>::MustBeTokenOwner1994        );19951996        // update balance1997        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1998            .checked_sub(1)1999            .ok_or(Error::<T>::NumOverflow)?;2000        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20012002        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2003            .checked_add(1)2004            .ok_or(Error::<T>::NumOverflow)?;2005        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20062007        // change owner2008        let old_owner = item.owner.clone();2009        item.owner = new_owner.clone();2010        <NftItemList<T>>::insert(collection_id, item_id, item);20112012        // update index collection2013        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;20142015        Ok(())2016    }2017    2018    fn item_exists(2019        collection_id: CollectionId,2020        item_id: TokenId,2021        mode: &CollectionMode2022    ) -> DispatchResult {2023        match mode {2024            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2025            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2026            _ => ()2027        };2028        2029        Ok(())2030    }20312032    fn set_re_fungible_variable_data(2033        collection_id: CollectionId,2034        item_id: TokenId,2035        data: Vec<u8>2036    ) -> DispatchResult {2037        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20382039        item.variable_data = data;20402041        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20422043        Ok(())2044    }20452046    fn set_nft_variable_data(2047        collection_id: CollectionId,2048        item_id: TokenId,2049        data: Vec<u8>2050    ) -> DispatchResult {2051        let mut item = <NftItemList<T>>::get(collection_id, item_id);2052        2053        item.variable_data = data;20542055        <NftItemList<T>>::insert(collection_id, item_id, item);2056        2057        Ok(())2058    }20592060    fn init_collection(item: &CollectionType<T::AccountId>) {2061        // check params2062        assert!(2063            item.decimal_points <= MAX_DECIMAL_POINTS,2064            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2065        );2066        assert!(2067            item.name.len() <= 64,2068            "Collection name can not be longer than 63 char"2069        );2070        assert!(2071            item.name.len() <= 256,2072            "Collection description can not be longer than 255 char"2073        );2074        assert!(2075            item.token_prefix.len() <= 16,2076            "Token prefix can not be longer than 15 char"2077        );20782079        // Generate next collection ID2080        let next_id = CreatedCollectionCount::get()2081            .checked_add(1)2082            .unwrap();20832084        CreatedCollectionCount::put(next_id);2085    }20862087    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2088        let current_index = <ItemListIndex>::get(collection_id)2089            .checked_add(1)2090            .unwrap();20912092        let item_owner = item.owner.clone();2093        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();20942095        <ItemListIndex>::insert(collection_id, current_index);20962097        // Update balance2098        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2099            .checked_add(1)2100            .unwrap();2101        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2102    }21032104    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2105        let current_index = <ItemListIndex>::get(collection_id)2106            .checked_add(1)2107            .unwrap();21082109        Self::add_token_index(collection_id, current_index, (*owner).clone()).unwrap();21102111        <ItemListIndex>::insert(collection_id, current_index);21122113        // Update balance2114        let new_balance = <Balance<T>>::get(collection_id, owner)2115            .checked_add(item.value)2116            .unwrap();2117        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2118    }21192120    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2121        let current_index = <ItemListIndex>::get(collection_id)2122            .checked_add(1)2123            .unwrap();21242125        let value = item.owner.first().unwrap().fraction;2126        let owner = item.owner.first().unwrap().owner.clone();21272128        Self::add_token_index(collection_id, current_index, owner.clone()).unwrap();21292130        <ItemListIndex>::insert(collection_id, current_index);21312132        // Update balance2133        let new_balance = <Balance<T>>::get(collection_id, owner.clone())2134            .checked_add(value)2135            .unwrap();2136        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2137    }21382139    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {21402141        // add to account limit2142        if <AccountItemCount<T>>::contains_key(owner.clone()) {21432144            // bound Owned tokens by a single address2145            let count = <AccountItemCount<T>>::get(owner.clone());2146            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21472148            <AccountItemCount<T>>::insert(owner.clone(), count2149                .checked_add(1)2150                .ok_or(Error::<T>::NumOverflow)?);2151        }2152        else {2153            <AccountItemCount<T>>::insert(owner.clone(), 1);2154        }21552156        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2157        if list_exists {2158            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2159            let item_contains = list.contains(&item_index.clone());21602161            if !item_contains {2162                list.push(item_index.clone());2163            }21642165            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2166        } else {2167            let mut itm = Vec::new();2168            itm.push(item_index.clone());2169            <AddressTokens<T>>::insert(collection_id, owner, itm);2170            2171        }21722173        Ok(())2174    }21752176    fn remove_token_index(2177        collection_id: CollectionId,2178        item_index: TokenId,2179        owner: T::AccountId,2180    ) -> DispatchResult {21812182        // update counter2183        <AccountItemCount<T>>::insert(owner.clone(), 2184            <AccountItemCount<T>>::get(owner.clone())2185            .checked_sub(1)2186            .ok_or(Error::<T>::NumOverflow)?);218721882189        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2190        if list_exists {2191            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2192            let item_contains = list.contains(&item_index.clone());21932194            if item_contains {2195                list.retain(|&item| item != item_index);2196                <AddressTokens<T>>::insert(collection_id, owner, list);2197            }2198        }21992200        Ok(())2201    }22022203    fn move_token_index(2204        collection_id: CollectionId,2205        item_index: TokenId,2206        old_owner: T::AccountId,2207        new_owner: T::AccountId,2208    ) -> DispatchResult {2209        Self::remove_token_index(collection_id, item_index, old_owner)?;2210        Self::add_token_index(collection_id, item_index, new_owner)?;22112212        Ok(())2213    }2214    2215    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2216        if <ContractOwner<T>>::contains_key(contract.clone()) {2217            let owner = <ContractOwner<T>>::get(contract);2218            ensure!(account == owner, Error::<T>::NoPermission);2219        } else {2220            fail!(Error::<T>::NoPermission);2221        }22222223        Ok(())2224    }2225}22262227////////////////////////////////////////////////////////////////////////////////////////////////////2228// Economic models2229// #region22302231/// Fee multiplier.2232pub type Multiplier = FixedU128;22332234type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2235    <T as system::Trait>::AccountId,2236>>::Balance;2237type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2238    <T as system::Trait>::AccountId,2239>>::NegativeImbalance;22402241/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2242/// in the queue.2243#[derive(Encode, Decode, Clone, Eq, PartialEq)]2244pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2245    #[codec(compact)] BalanceOf<T>2246);22472248impl<T: Trait + Send + Sync> sp_std::fmt::Debug2249    for ChargeTransactionPayment<T>2250{2251    #[cfg(feature = "std")]2252    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2253        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2254    }2255    #[cfg(not(feature = "std"))]2256    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2257        Ok(())2258    }2259}22602261impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2262where2263    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2264    BalanceOf<T>: Send + Sync + FixedPointOperand,2265{2266    /// utility constructor. Used only in client/factory code.2267    pub fn from(fee: BalanceOf<T>) -> Self {2268        Self(fee)2269    }22702271    pub fn traditional_fee(2272        len: usize,2273        info: &DispatchInfoOf<T::Call>,2274        tip: BalanceOf<T>,2275    ) -> BalanceOf<T>2276    where2277        T::Call: Dispatchable<Info = DispatchInfo>,2278    {2279        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2280    }22812282	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2283		let weight_saturation = T::MaximumBlockWeight::get() / info.weight.max(1);2284		let len_saturation = T::MaximumBlockLength::get() as u64 / (len as u64).max(1);2285		let coefficient: BalanceOf<T> = weight_saturation.min(len_saturation).saturated_into::<BalanceOf<T>>();2286		final_fee.saturating_mul(coefficient).saturated_into::<TransactionPriority>()2287	}22882289    fn withdraw_fee(2290        &self,2291        who: &T::AccountId,2292        call: &T::Call,2293        info: &DispatchInfoOf<T::Call>,2294        len: usize,2295    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2296        let tip = self.0;22972298        // Set fee based on call type. Creating collection costs 1 Unique.2299        // All other transactions have traditional fees so far2300        // let fee = match call.is_sub_type() {2301        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2302        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2303        //                                                 // _ => <BalanceOf<T>>::from(100)2304        // };2305        let fee = Self::traditional_fee(len, info, tip);23062307        // Only mess with balances if fee is not zero.2308        if fee.is_zero() {2309            return Ok((fee, None));2310        }23112312        // Determine who is paying transaction fee based on ecnomic model2313        // Parse call to extract collection ID and access collection sponsor2314        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2315            Some(Call::create_item(collection_id, _owner, _properties)) => {23162317                // sponsor timeout2318                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;23192320                let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;2321                let mut sponsored = true;2322                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2323                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2324                    let limit_time = last_tx_block + limit.into();2325                    if block_number <= limit_time {2326                        sponsored = false;2327                    }2328                }2329                if sponsored {2330                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2331                }23322333                // check free create limit2334                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2335                   (<Collection<T>>::get(collection_id).sponsor_confirmed) &&2336                   (sponsored)2337                {2338                    <Collection<T>>::get(collection_id).sponsor2339                } else {2340                    T::AccountId::default()2341                }2342            }2343            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2344                2345                let mut sponsor_transfer = false;2346                if <Collection<T>>::get(collection_id).sponsor_confirmed {23472348                    let collection_limits = <Collection<T>>::get(collection_id).limits;2349                    let collection_mode = <Collection<T>>::get(collection_id).mode;2350    2351                    // sponsor timeout2352                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2353                    sponsor_transfer = match collection_mode {2354                        CollectionMode::NFT => {2355    2356                            // get correct limit2357                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2358                                collection_limits.sponsor_transfer_timeout2359                            } else {2360                                ChainLimit::get().nft_sponsor_transfer_timeout2361                            };2362    2363                            let mut sponsored = true;2364                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2365                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2366                                let limit_time = last_tx_block + limit.into();2367                                if block_number <= limit_time {2368                                    sponsored = false;2369                                }2370                            }2371                            if sponsored {2372                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2373                            }23742375                            sponsored2376                        }2377                        CollectionMode::Fungible(_) => {2378    2379                            // get correct limit2380                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2381                                collection_limits.sponsor_transfer_timeout2382                            } else {2383                                ChainLimit::get().fungible_sponsor_transfer_timeout2384                            };2385    2386                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2387                            let mut sponsored = true;2388                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2389                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2390                                let limit_time = last_tx_block + limit.into();2391                                if block_number <= limit_time {2392                                    sponsored = false;2393                                }2394                            }2395                            if sponsored {2396                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2397                            }23982399                            sponsored2400                        }2401                        CollectionMode::ReFungible(_) => {2402    2403                            // get correct limit2404                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2405                                collection_limits.sponsor_transfer_timeout2406                            } else {2407                                ChainLimit::get().refungible_sponsor_transfer_timeout2408                            };2409    2410                            let mut sponsored = true;2411                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2412                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2413                                let limit_time = last_tx_block + limit.into();2414                                if block_number <= limit_time {2415                                    sponsored = false;2416                                }2417                            }2418                            if sponsored {2419                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2420                            }24212422                            sponsored2423                        }2424                        _ => {2425                            false2426                        },2427                    };2428                }24292430                if !sponsor_transfer {2431                    T::AccountId::default()2432                } else {2433                    <Collection<T>>::get(collection_id).sponsor2434                }2435            }24362437            _ => T::AccountId::default(),2438        };24392440        // Sponsor smart contracts2441        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {24422443            // On instantiation: set the contract owner2444            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {24452446                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2447                    code_hash,2448                    &data,2449                    &who,2450                );2451                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());24522453                T::AccountId::default()2454            },24552456            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2457            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {24582459                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());24602461                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2462                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2463                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2464                  2465                if !owned_contract && white_list_enabled {2466                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2467                        return Err(InvalidTransaction::Call.into());2468                    }2469                }24702471                let mut sponsor_transfer = false;2472                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2473                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2474                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2475                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2476                    let limit_time = last_tx_block + rate_limit;24772478                    if block_number >= limit_time {2479                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2480                        sponsor_transfer = true;2481                    }2482                } else {2483                    sponsor_transfer = false;2484                }2485               2486                2487                let mut sp = T::AccountId::default();2488                if sponsor_transfer {2489                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2490                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2491                            sp = called_contract;2492                        }2493                    }2494                }24952496                sp2497            },24982499            _ => sponsor,2500        };25012502        let mut who_pays_fee: T::AccountId = sponsor.clone();2503        if sponsor == T::AccountId::default() {2504            who_pays_fee = who.clone();2505        }25062507        match <T as transaction_payment::Trait>::Currency::withdraw(2508            &who_pays_fee,2509            fee,2510            if tip.is_zero() {2511                WithdrawReason::TransactionPayment.into()2512            } else {2513                WithdrawReason::TransactionPayment | WithdrawReason::Tip2514            },2515            ExistenceRequirement::KeepAlive,2516        ) {2517            Ok(imbalance) => Ok((fee, Some(imbalance))),2518            Err(_) => Err(InvalidTransaction::Payment.into()),2519        }2520    }2521}252225232524impl<T: Trait + Send + Sync> SignedExtension2525    for ChargeTransactionPayment<T>2526where2527    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2528    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2529{2530    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2531    type AccountId = T::AccountId;2532    type Call = T::Call;2533    type AdditionalSigned = ();2534    type Pre = (2535        BalanceOf<T>,2536        Self::AccountId,2537        Option<NegativeImbalanceOf<T>>,2538        BalanceOf<T>,2539    );2540    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2541        Ok(())2542    }25432544    fn validate(2545        &self,2546        who: &Self::AccountId,2547        call: &Self::Call,2548        info: &DispatchInfoOf<Self::Call>,2549        len: usize,2550    ) -> TransactionValidity {2551		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2552		Ok(ValidTransaction {2553			priority: Self::get_priority(len, info, fee),2554			..Default::default()2555		})2556    }25572558    fn pre_dispatch(2559        self,2560        who: &Self::AccountId,2561        call: &Self::Call,2562        info: &DispatchInfoOf<Self::Call>,2563        len: usize,2564    ) -> Result<Self::Pre, TransactionValidityError> {2565        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2566        Ok((self.0, who.clone(), imbalance, fee))2567    }25682569    fn post_dispatch(2570        pre: Self::Pre,2571        info: &DispatchInfoOf<Self::Call>,2572        post_info: &PostDispatchInfoOf<Self::Call>,2573        len: usize,2574        _result: &DispatchResult,2575    ) -> Result<(), TransactionValidityError> {2576        let (tip, who, imbalance, fee) = pre;2577        if let Some(payed) = imbalance {2578            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2579                len as u32, info, post_info, tip,2580            );2581            let refund = fee.saturating_sub(actual_fee);2582            let actual_payment =2583                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2584                    &who, refund,2585                ) {2586                    Ok(refund_imbalance) => {2587                        // The refund cannot be larger than the up front payed max weight.2588                        // `PostDispatchInfo::calc_unspent` guards against such a case.2589                        match payed.offset(refund_imbalance) {2590                            Ok(actual_payment) => actual_payment,2591                            Err(_) => return Err(InvalidTransaction::Payment.into()),2592                        }2593                    }2594                    // We do not recreate the account using the refund. The up front payment2595                    // is gone in that case.2596                    Err(_) => payed,2597                };2598            let imbalances = actual_payment.split(tip);2599            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2600                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2601            );2602        }2603        Ok(())2604    }2605}26062607// #endregion
after · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, WithdrawReason,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial,29    },30    IsSubType, 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 pallet_contracts::ContractAddressFor;45use sp_runtime::traits::StaticLookup;4647#[cfg(test)]48mod mock;4950#[cfg(test)]51mod tests;5253mod default_weights;5455pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;56pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;57pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;5859// Structs60// #region6162pub type CollectionId = u32;63pub type TokenId = u32;64pub type DecimalPoints = u8;6566#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]67#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]68pub enum CollectionMode {69    Invalid,70    NFT,71    // decimal points72    Fungible(DecimalPoints),73    // decimal points74    ReFungible(DecimalPoints),75}7677impl Default for CollectionMode {78    fn default() -> Self {79        Self::Invalid80    }81}8283impl Into<u8> for CollectionMode {84    fn into(self) -> u8 {85        match self {86            CollectionMode::Invalid => 0,87            CollectionMode::NFT => 1,88            CollectionMode::Fungible(_) => 2,89            CollectionMode::ReFungible(_) => 3,90        }91    }92}9394#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]95#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]96pub enum AccessMode {97    Normal,98    WhiteList,99}100impl Default for AccessMode {101    fn default() -> Self {102        Self::Normal103    }104}105106#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]107#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]108pub enum SchemaVersion {109    ImageURL,110    Unique,111}112impl Default for SchemaVersion {113    fn default() -> Self {114        Self::ImageURL115    }116}117118#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]119#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]120pub struct Ownership<AccountId> {121    pub owner: AccountId,122    pub fraction: u128,123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct CollectionType<AccountId> {128    pub owner: AccountId,129    pub mode: CollectionMode,130    pub access: AccessMode,131    pub decimal_points: DecimalPoints,132    pub name: Vec<u16>,        // 64 include null escape char133    pub description: Vec<u16>, // 256 include null escape char134    pub token_prefix: Vec<u8>, // 16 include null escape char135    pub mint_mode: bool,136    pub offchain_schema: Vec<u8>,137    pub schema_version: SchemaVersion,138    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender139    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.140    pub limits: CollectionLimits, // Collection private restrictions 141    pub variable_on_chain_schema: Vec<u8>, //142    pub const_on_chain_schema: Vec<u8>, //143}144145#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]146#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]147pub struct NftItemType<AccountId> {148    pub owner: AccountId,149    pub const_data: Vec<u8>,150    pub variable_data: Vec<u8>,151}152153#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]154#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]155pub struct FungibleItemType {156    pub value: u128,157}158159#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]160#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]161pub struct ReFungibleItemType<AccountId> {162    pub owner: Vec<Ownership<AccountId>>,163    pub const_data: Vec<u8>,164    pub variable_data: Vec<u8>,165}166167// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]168// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169// pub struct VestingItem<AccountId, Moment> {170//     pub sender: AccountId,171//     pub recipient: AccountId,172//     pub collection_id: CollectionId,173//     pub item_id: TokenId,174//     pub amount: u64,175//     pub vesting_date: Moment,176// }177178#[derive(Encode, Decode, Debug, Clone, PartialEq)]179#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]180pub struct CollectionLimits {181    pub account_token_ownership_limit: u32,182    pub sponsored_data_size: u32,183    pub token_limit: u32,184185    // Timeouts for item types in passed blocks186    pub sponsor_transfer_timeout: u32,187}188189impl Default for CollectionLimits {190    fn default() -> CollectionLimits {191        CollectionLimits { 192            account_token_ownership_limit: 10_000_000, 193            token_limit: u32::max_value(),194            sponsored_data_size: u32::max_value(), 195            sponsor_transfer_timeout: 14400 }196    }197}198199#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]200#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]201pub struct ChainLimits {202    pub collection_numbers_limit: u32,203    pub account_token_ownership_limit: u32,204    pub collections_admins_limit: u64,205    pub custom_data_limit: u32,206207    // Timeouts for item types in passed blocks208    pub nft_sponsor_transfer_timeout: u32,209    pub fungible_sponsor_transfer_timeout: u32,210    pub refungible_sponsor_transfer_timeout: u32,211212    // Schema limits213    pub offchain_schema_limit: u32,214    pub variable_on_chain_schema_limit: u32,215    pub const_on_chain_schema_limit: u32,216}217218pub trait WeightInfo {219	fn create_collection() -> Weight;220	fn destroy_collection() -> Weight;221	fn add_to_white_list() -> Weight;222	fn remove_from_white_list() -> Weight;223    fn set_public_access_mode() -> Weight;224    fn set_mint_permission() -> Weight;225    fn change_collection_owner() -> Weight;226    fn add_collection_admin() -> Weight;227    fn remove_collection_admin() -> Weight;228    fn set_collection_sponsor() -> Weight;229    fn confirm_sponsorship() -> Weight;230    fn remove_collection_sponsor() -> Weight;231    fn create_item(s: usize) -> Weight;232    fn burn_item() -> Weight;233    fn transfer() -> Weight;234    fn approve() -> Weight;235    fn transfer_from() -> Weight;236    fn set_offchain_schema() -> Weight;237    fn set_const_on_chain_schema() -> Weight;238    fn set_variable_on_chain_schema() -> Weight;239    fn set_variable_meta_data() -> Weight;240    fn enable_contract_sponsoring() -> Weight;241    fn set_schema_version() -> Weight;242    fn set_chain_limits() -> Weight;243    fn set_contract_sponsoring_rate_limit() -> Weight;244    fn toggle_contract_white_list() -> Weight;245    fn add_to_contract_white_list() -> Weight;246    fn remove_from_contract_white_list() -> Weight;247    fn set_collection_limits() -> Weight;248}249250#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]251#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]252pub struct CreateNftData {253    pub const_data: Vec<u8>,254    pub variable_data: Vec<u8>,255}256257#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]258#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]259pub struct CreateFungibleData {260    pub value: u128,261}262263#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]264#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]265pub struct CreateReFungibleData {266    pub const_data: Vec<u8>,267    pub variable_data: Vec<u8>,268}269270#[derive(Encode, Decode, Debug, Clone, PartialEq)]271#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]272pub enum CreateItemData {273    NFT(CreateNftData),274    Fungible(CreateFungibleData),275    ReFungible(CreateReFungibleData),276}277278impl CreateItemData {279    pub fn len(&self) -> usize {280        let len = match self {281            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),282            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),283            _ => 0284        };285        286        return len;287    }288}289290impl From<CreateNftData> for CreateItemData {291    fn from(item: CreateNftData) -> Self {292        CreateItemData::NFT(item)293    }294}295296impl From<CreateReFungibleData> for CreateItemData {297    fn from(item: CreateReFungibleData) -> Self {298        CreateItemData::ReFungible(item)299    }300}301302impl From<CreateFungibleData> for CreateItemData {303    fn from(item: CreateFungibleData) -> Self {304        CreateItemData::Fungible(item)305    }306}307308309decl_error! {310	/// Error for non-fungible-token module.311	pub enum Error for Module<T: Trait> {312        /// Total collections bound exceeded.313        TotalCollectionsLimitExceeded,314		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.315        CollectionDecimalPointLimitExceeded, 316        /// Collection name can not be longer than 63 char.317        CollectionNameLimitExceeded, 318        /// Collection description can not be longer than 255 char.319        CollectionDescriptionLimitExceeded, 320        /// Token prefix can not be longer than 15 char.321        CollectionTokenPrefixLimitExceeded,322        /// This collection does not exist.323        CollectionNotFound,324        /// Item not exists.325        TokenNotFound,326        /// Arithmetic calculation overflow.327        NumOverflow,       328        /// Account already has admin role.329        AlreadyAdmin,  330        /// You do not own this collection.331        NoPermission,332        /// This address is not set as sponsor, use setCollectionSponsor first.333        ConfirmUnsetSponsorFail,334        /// Collection is not in mint mode.335        PublicMintingNotAllowed,336        /// Sender parameter and item owner must be equal.337        MustBeTokenOwner,338        /// Item balance not enough.339        TokenValueTooLow,340        /// Size of item is too large.341        NftSizeLimitExceeded,342        /// No approve found343        ApproveNotFound,344        /// Requested value more than approved.345        TokenValueNotEnough,346        /// Only approved addresses can call this method.347        ApproveRequired,348        /// Address is not in white list.349        AddresNotInWhiteList,350        /// Number of collection admins bound exceeded.351        CollectionAdminsLimitExceeded,352        /// Owned tokens by a single address bound exceeded.353        AddressOwnershipLimitExceeded,354        /// Length of items properties must be greater than 0.355        EmptyArgument,356        /// const_data exceeded data limit.357        TokenConstDataLimitExceeded,358        /// variable_data exceeded data limit.359        TokenVariableDataLimitExceeded,360        /// Not NFT item data used to mint in NFT collection.361        NotNftDataUsedToMintNftCollectionToken,362        /// Not Fungible item data used to mint in Fungible collection.363        NotFungibleDataUsedToMintFungibleCollectionToken,364        /// Not Re Fungible item data used to mint in Re Fungible collection.365        NotReFungibleDataUsedToMintReFungibleCollectionToken,366        /// Unexpected collection type.367        UnexpectedCollectionType,368        /// Can't store metadata in fungible tokens.369        CantStoreMetadataInFungibleTokens,370        /// Collection token limit exceeded371        CollectionTokenLimitExceeded,372        /// Account token limit exceeded per collection373        AccountTokenLimitExceeded,374        /// Collection limit bounds per collection exceeded375        CollectionLimitBoundsExceeded,376        /// Schema data size limit bound exceeded377        SchemaDataLimitExceeded378	}379}380381pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {382    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;383384    /// Weight information for extrinsics in this pallet.385	type WeightInfo: WeightInfo;386}387388#[cfg(feature = "runtime-benchmarks")]389mod benchmarking;390391// #endregion392393decl_storage! {394    trait Store for Module<T: Trait> as Nft {395396        // Private members397        NextCollectionID: CollectionId;398        CreatedCollectionCount: u32;399        ChainVersion: u64;400        ItemListIndex: map hasher(identity) CollectionId => TokenId;401402        // Chain limits struct403        pub ChainLimit get(fn chain_limit) config(): ChainLimits;404405        // Bound counters406        CollectionCount: u32;407        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;408409        // Basic collections410        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;411        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;412        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;413414        /// Balance owner per collection map415        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;416417        /// second parameter: item id + owner account id + spender account id418        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;419420        /// Item collections421        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;422        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;423        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;424425        /// Index list426        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;427428        /// Tokens transfer baskets429        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;430        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;431        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;432        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;433434        // Contract Sponsorship and Ownership435        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;436        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;437        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;438        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;439        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 440        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 441    }442    add_extra_genesis {443        build(|config: &GenesisConfig<T>| {444            // Modification of storage445            for (_num, _c) in &config.collection {446                <Module<T>>::init_collection(_c);447            }448449            for (_num, _c, _i) in &config.nft_item_id {450                <Module<T>>::init_nft_token(*_c, _i);451            }452453            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {454                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);455            }456457            for (_num, _c, _i) in &config.refungible_item_id {458                <Module<T>>::init_refungible_token(*_c, _i);459            }460        })461    }462}463464decl_event!(465    pub enum Event<T>466    where467        AccountId = <T as system::Trait>::AccountId,468    {469        /// New collection was created470        /// 471        /// # Arguments472        /// 473        /// * collection_id: Globally unique identifier of newly created collection.474        /// 475        /// * mode: [CollectionMode] converted into u8.476        /// 477        /// * account_id: Collection owner.478        Created(CollectionId, u8, AccountId),479480        /// New item was created.481        /// 482        /// # Arguments483        /// 484        /// * collection_id: Id of the collection where item was created.485        /// 486        /// * item_id: Id of an item. Unique within the collection.487        ItemCreated(CollectionId, TokenId),488489        /// Collection item was burned.490        /// 491        /// # Arguments492        /// 493        /// collection_id.494        /// 495        /// item_id: Identifier of burned NFT.496        ItemDestroyed(CollectionId, TokenId),497    }498);499500decl_module! {501    pub struct Module<T: Trait> for enum Call where origin: T::Origin {502503        fn deposit_event() = default;504        type Error = Error<T>;505506        fn on_initialize(now: T::BlockNumber) -> Weight {507508            if ChainVersion::get() < 2509            {510                let value = NextCollectionID::get();511                CreatedCollectionCount::put(value);512                ChainVersion::put(2);513            }514515            0516        }517518        /// 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.519        /// 520        /// # Permissions521        /// 522        /// * Anyone.523        /// 524        /// # Arguments525        /// 526        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.527        /// 528        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.529        /// 530        /// * token_prefix: UTF-8 string with token prefix.531        /// 532        /// * mode: [CollectionMode] collection type and type dependent data.533        // returns collection ID534        #[weight = T::WeightInfo::create_collection()]535        pub fn create_collection(origin,536                                 collection_name: Vec<u16>,537                                 collection_description: Vec<u16>,538                                 token_prefix: Vec<u8>,539                                 mode: CollectionMode) -> DispatchResult {540541            // Anyone can create a collection542            let who = ensure_signed(origin)?;543544            let decimal_points = match mode {545                CollectionMode::Fungible(points) => points,546                CollectionMode::ReFungible(points) => points,547                _ => 0548            };549550            // bound Total number of collections551            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);552553            // check params554            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);555            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);556            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);557            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);558559            // Generate next collection ID560            let next_id = CreatedCollectionCount::get()561                .checked_add(1)562                .ok_or(Error::<T>::NumOverflow)?;563564            // bound counter565            let total = CollectionCount::get()566                .checked_add(1)567                .ok_or(Error::<T>::NumOverflow)?;568569            CreatedCollectionCount::put(next_id);570            CollectionCount::put(total);571572            // Create new collection573            let new_collection = CollectionType {574                owner: who.clone(),575                name: collection_name,576                mode: mode.clone(),577                mint_mode: false,578                access: AccessMode::Normal,579                description: collection_description,580                decimal_points: decimal_points,581                token_prefix: token_prefix,582                offchain_schema: Vec::new(),583                schema_version: SchemaVersion::ImageURL,584                sponsor: T::AccountId::default(),585                sponsor_confirmed: false,586                variable_on_chain_schema: Vec::new(),587                const_on_chain_schema: Vec::new(),588                limits: CollectionLimits::default(),589            };590591            // Add new collection to map592            <Collection<T>>::insert(next_id, new_collection);593594            // call event595            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));596597            Ok(())598        }599600        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.601        /// 602        /// # Permissions603        /// 604        /// * Collection Owner.605        /// 606        /// # Arguments607        /// 608        /// * collection_id: collection to destroy.609        #[weight = T::WeightInfo::destroy_collection()]610        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {611612            let sender = ensure_signed(origin)?;613            Self::check_owner_permissions(collection_id, sender)?;614615            <AddressTokens<T>>::remove_prefix(collection_id);616            <Allowances<T>>::remove_prefix(collection_id);617            <Balance<T>>::remove_prefix(collection_id);618            <ItemListIndex>::remove(collection_id);619            <AdminList<T>>::remove(collection_id);620            <Collection<T>>::remove(collection_id);621            <WhiteList<T>>::remove_prefix(collection_id);622623            <NftItemList<T>>::remove_prefix(collection_id);624            <FungibleItemList<T>>::remove_prefix(collection_id);625            <ReFungibleItemList<T>>::remove_prefix(collection_id);626627            <NftTransferBasket<T>>::remove_prefix(collection_id);628            <FungibleTransferBasket<T>>::remove_prefix(collection_id);629            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);630631            if CollectionCount::get() > 0632            {633                // bound couter634                let total = CollectionCount::get()635                    .checked_sub(1)636                    .ok_or(Error::<T>::NumOverflow)?;637638                CollectionCount::put(total);639            }640641            Ok(())642        }643644        /// Add an address to white list.645        /// 646        /// # Permissions647        /// 648        /// * Collection Owner649        /// * Collection Admin650        /// 651        /// # Arguments652        /// 653        /// * collection_id.654        /// 655        /// * address.656        #[weight = T::WeightInfo::add_to_white_list()]657        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{658659            let sender = ensure_signed(origin)?;660            Self::check_owner_or_admin_permissions(collection_id, sender)?;661662            <WhiteList<T>>::insert(collection_id, address, true);663            664            Ok(())665        }666667        /// Remove an address from white list.668        /// 669        /// # Permissions670        /// 671        /// * Collection Owner672        /// * Collection Admin673        /// 674        /// # Arguments675        /// 676        /// * collection_id.677        /// 678        /// * address.679        #[weight = T::WeightInfo::remove_from_white_list()]680        pub fn remove_from_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>>::remove(collection_id, address);686687            Ok(())688        }689690        /// Toggle between normal and white list access for the methods with access for `Anyone`.691        /// 692        /// # Permissions693        /// 694        /// * Collection Owner.695        /// 696        /// # Arguments697        /// 698        /// * collection_id.699        /// 700        /// * mode: [AccessMode]701        #[weight = T::WeightInfo::set_public_access_mode()]702        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult703        {704            let sender = ensure_signed(origin)?;705706            Self::check_owner_permissions(collection_id, sender)?;707            let mut target_collection = <Collection<T>>::get(collection_id);708            target_collection.access = mode;709            <Collection<T>>::insert(collection_id, target_collection);710711            Ok(())712        }713714        /// Allows Anyone to create tokens if:715        /// * White List is enabled, and716        /// * Address is added to white list, and717        /// * This method was called with True parameter718        /// 719        /// # Permissions720        /// * Collection Owner721        ///722        /// # Arguments723        /// 724        /// * collection_id.725        /// 726        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.727        #[weight = T::WeightInfo::set_mint_permission()]728        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult729        {730            let sender = ensure_signed(origin)?;731732            Self::check_owner_permissions(collection_id, sender)?;733            let mut target_collection = <Collection<T>>::get(collection_id);734            target_collection.mint_mode = mint_permission;735            <Collection<T>>::insert(collection_id, target_collection);736737            Ok(())738        }739740        /// Change the owner of the collection.741        /// 742        /// # Permissions743        /// 744        /// * Collection Owner.745        /// 746        /// # Arguments747        /// 748        /// * collection_id.749        /// 750        /// * new_owner.751        #[weight = T::WeightInfo::change_collection_owner()]752        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {753754            let sender = ensure_signed(origin)?;755            Self::check_owner_permissions(collection_id, sender)?;756            let mut target_collection = <Collection<T>>::get(collection_id);757            target_collection.owner = new_owner;758            <Collection<T>>::insert(collection_id, target_collection);759760            Ok(())761        }762763        /// Adds an admin of the Collection.764        /// 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. 765        /// 766        /// # Permissions767        /// 768        /// * Collection Owner.769        /// * Collection Admin.770        /// 771        /// # Arguments772        /// 773        /// * collection_id: ID of the Collection to add admin for.774        /// 775        /// * new_admin_id: Address of new admin to add.776        #[weight = T::WeightInfo::add_collection_admin()]777        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {778779            let sender = ensure_signed(origin)?;780            Self::check_owner_or_admin_permissions(collection_id, sender)?;781            let mut admin_arr: Vec<T::AccountId> = Vec::new();782783            if <AdminList<T>>::contains_key(collection_id)784            {785                admin_arr = <AdminList<T>>::get(collection_id);786                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);787            }788789            // Number of collection admins790            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);791792            admin_arr.push(new_admin_id);793            <AdminList<T>>::insert(collection_id, admin_arr);794795            Ok(())796        }797798        /// 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.799        ///800        /// # Permissions801        /// 802        /// * Collection Owner.803        /// * Collection Admin.804        /// 805        /// # Arguments806        /// 807        /// * collection_id: ID of the Collection to remove admin for.808        /// 809        /// * account_id: Address of admin to remove.810        #[weight = T::WeightInfo::remove_collection_admin()]811        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {812813            let sender = ensure_signed(origin)?;814            Self::check_owner_or_admin_permissions(collection_id, sender)?;815816            if <AdminList<T>>::contains_key(collection_id)817            {818                let mut admin_arr = <AdminList<T>>::get(collection_id);819                admin_arr.retain(|i| *i != account_id);820                <AdminList<T>>::insert(collection_id, admin_arr);821            }822823            Ok(())824        }825826        /// # Permissions827        /// 828        /// * Collection Owner829        /// 830        /// # Arguments831        /// 832        /// * collection_id.833        /// 834        /// * new_sponsor.835        #[weight = T::WeightInfo::set_collection_sponsor()]836        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {837838            let sender = ensure_signed(origin)?;839            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);840841            let mut target_collection = <Collection<T>>::get(collection_id);842            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);843844            target_collection.sponsor = new_sponsor;845            target_collection.sponsor_confirmed = false;846            <Collection<T>>::insert(collection_id, target_collection);847848            Ok(())849        }850851        /// # Permissions852        /// 853        /// * Sponsor.854        /// 855        /// # Arguments856        /// 857        /// * collection_id.858        #[weight = T::WeightInfo::confirm_sponsorship()]859        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {860861            let sender = ensure_signed(origin)?;862            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);863864            let mut target_collection = <Collection<T>>::get(collection_id);865            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);866867            target_collection.sponsor_confirmed = true;868            <Collection<T>>::insert(collection_id, target_collection);869870            Ok(())871        }872873        /// Switch back to pay-per-own-transaction model.874        ///875        /// # Permissions876        ///877        /// * Collection owner.878        /// 879        /// # Arguments880        /// 881        /// * collection_id.882        #[weight = T::WeightInfo::remove_collection_sponsor()]883        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {884885            let sender = ensure_signed(origin)?;886            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);887888            let mut target_collection = <Collection<T>>::get(collection_id);889            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);890891            target_collection.sponsor = T::AccountId::default();892            target_collection.sponsor_confirmed = false;893            <Collection<T>>::insert(collection_id, target_collection);894895            Ok(())896        }897898        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.899        /// 900        /// # Permissions901        /// 902        /// * Collection Owner.903        /// * Collection Admin.904        /// * Anyone if905        ///     * White List is enabled, and906        ///     * Address is added to white list, and907        ///     * MintPermission is enabled (see SetMintPermission method)908        /// 909        /// # Arguments910        /// 911        /// * collection_id: ID of the collection.912        /// 913        /// * owner: Address, initial owner of the NFT.914        ///915        /// * data: Token data to store on chain.916        // #[weight =917        // (130_000_000 as Weight)918        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))919        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))920        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]921922        #[weight = T::WeightInfo::create_item(data.len())]923        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {924925            let sender = ensure_signed(origin)?;926927            Self::collection_exists(collection_id)?;928929            let target_collection = <Collection<T>>::get(collection_id);930931            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;932            Self::validate_create_item_args(&target_collection, &data)?;933            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;934935            Ok(())936        }937938        /// This method creates multiple instances of NFT Collection created with CreateCollection method.939        /// 940        /// # Permissions941        /// 942        /// * Collection Owner.943        /// * Collection Admin.944        /// * Anyone if945        ///     * White List is enabled, and946        ///     * Address is added to white list, and947        ///     * MintPermission is enabled (see SetMintPermission method)948        /// 949        /// # Arguments950        /// 951        /// * collection_id: ID of the collection.952        /// 953        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].954        /// 955        /// * owner: Address, initial owner of the NFT.956        #[weight = T::WeightInfo::create_item(items_data.into_iter()957                               .map(|data| { data.len() })958                               .sum())]959        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {960961            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);962            let sender = ensure_signed(origin)?;963964            Self::collection_exists(collection_id)?;965            let target_collection = <Collection<T>>::get(collection_id);966967            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;968969            for data in &items_data {970                Self::validate_create_item_args(&target_collection, data)?;971            }972            for data in &items_data {973                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;974            }975976            Ok(())977        }978979        /// Destroys a concrete instance of NFT.980        /// 981        /// # Permissions982        /// 983        /// * Collection Owner.984        /// * Collection Admin.985        /// * Current NFT Owner.986        /// 987        /// # Arguments988        /// 989        /// * collection_id: ID of the collection.990        /// 991        /// * item_id: ID of NFT to burn.992        #[weight = T::WeightInfo::burn_item()]993        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {994995            let sender = ensure_signed(origin)?;996            Self::collection_exists(collection_id)?;997998            // Transfer permissions check999            let target_collection = <Collection<T>>::get(collection_id);1000            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1001                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1002                Error::<T>::NoPermission);10031004            if target_collection.access == AccessMode::WhiteList {1005                Self::check_white_list(collection_id, &sender)?;1006            }10071008            match target_collection.mode1009            {1010                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1011                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1012                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, &sender)?,1013                _ => ()1014            };10151016            // call event1017            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10181019            Ok(())1020        }10211022        /// Change ownership of the token.1023        /// 1024        /// # Permissions1025        /// 1026        /// * Collection Owner1027        /// * Collection Admin1028        /// * Current NFT owner1029        ///1030        /// # Arguments1031        /// 1032        /// * recipient: Address of token recipient.1033        /// 1034        /// * collection_id.1035        /// 1036        /// * item_id: ID of the item1037        ///     * Non-Fungible Mode: Required.1038        ///     * Fungible Mode: Ignored.1039        ///     * Re-Fungible Mode: Required.1040        /// 1041        /// * value: Amount to transfer.1042        ///     * Non-Fungible Mode: Ignored1043        ///     * Fungible Mode: Must specify transferred amount1044        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1045        #[weight = T::WeightInfo::transfer()]1046        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10471048            let sender = ensure_signed(origin)?;1049            let target_collection = <Collection<T>>::get(collection_id);10501051            // Limits check1052            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;10531054            // Transfer permissions check1055            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1056                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1057                Error::<T>::NoPermission);10581059            if target_collection.access == AccessMode::WhiteList {1060                Self::check_white_list(collection_id, &sender)?;1061                Self::check_white_list(collection_id, &recipient)?;1062            }10631064            match target_collection.mode1065            {1066                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1067                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1068                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1069                _ => ()1070            };10711072            Ok(())1073        }10741075        /// Set, change, or remove approved address to transfer the ownership of the NFT.1076        /// 1077        /// # Permissions1078        /// 1079        /// * Collection Owner1080        /// * Collection Admin1081        /// * Current NFT owner1082        /// 1083        /// # Arguments1084        /// 1085        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1086        /// 1087        /// * collection_id.1088        /// 1089        /// * item_id: ID of the item.1090        #[weight = T::WeightInfo::approve()]1091        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {10921093            let sender = ensure_signed(origin)?;10941095            Self::collection_exists(collection_id)?;1096            Self::token_exists(collection_id, item_id, &sender)?;10971098            // Transfer permissions check1099            let target_collection = <Collection<T>>::get(collection_id);1100            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1101                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1102                Error::<T>::NoPermission);11031104            if target_collection.access == AccessMode::WhiteList {1105                Self::check_white_list(collection_id, &sender)?;1106                Self::check_white_list(collection_id, &spender)?;1107            }11081109            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1110            let mut allowance: u128 = amount;1111            if allowance_exists {1112                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1113            }1114            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11151116            Ok(())1117        }1118        1119        /// 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.1120        /// 1121        /// # Permissions1122        /// * Collection Owner1123        /// * Collection Admin1124        /// * Current NFT owner1125        /// * Address approved by current NFT owner1126        /// 1127        /// # Arguments1128        /// 1129        /// * from: Address that owns token.1130        /// 1131        /// * recipient: Address of token recipient.1132        /// 1133        /// * collection_id.1134        /// 1135        /// * item_id: ID of the item.1136        /// 1137        /// * value: Amount to transfer.1138        #[weight = T::WeightInfo::transfer_from()]1139        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11401141            let sender = ensure_signed(origin)?;1142            let mut appoved_transfer = false;11431144            // Check approval1145            let mut approval: u128 = 0;1146            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &recipient)) {1147                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &recipient));1148                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1149                appoved_transfer = true;1150            }11511152            let target_collection = <Collection<T>>::get(collection_id);11531154            // Limits check1155            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11561157            // Transfer permissions check         1158            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1159                Error::<T>::NoPermission);11601161            if target_collection.access == AccessMode::WhiteList {1162                Self::check_white_list(collection_id, &sender)?;1163                Self::check_white_list(collection_id, &recipient)?;1164            }11651166            // Reduce approval by transferred amount or remove if remaining approval drops to 01167            if approval.checked_sub(value).unwrap_or(0) > 0 {1168                <Allowances<T>>::insert(collection_id, (item_id, &from, &recipient), approval - value);1169            }1170            else {1171                <Allowances<T>>::remove(collection_id, (item_id, &from, &recipient));1172            }11731174            match target_collection.mode1175            {1176                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1177                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1178                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1179                _ => ()1180            };11811182            Ok(())1183        }11841185        #[weight = 0]1186        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {11871188            // let no_perm_mes = "You do not have permissions to modify this collection";1189            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1190            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1191            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11921193            // // on_nft_received  call11941195            // Self::transfer(origin, collection_id, item_id, new_owner)?;11961197            Ok(())1198        }11991200        /// Set off-chain data schema.1201        /// 1202        /// # Permissions1203        /// 1204        /// * Collection Owner1205        /// * Collection Admin1206        /// 1207        /// # Arguments1208        /// 1209        /// * collection_id.1210        /// 1211        /// * schema: String representing the offchain data schema.1212        #[weight = T::WeightInfo::set_variable_meta_data()]1213        pub fn set_variable_meta_data (1214            origin,1215            collection_id: CollectionId,1216            item_id: TokenId,1217            data: Vec<u8>1218        ) -> DispatchResult {1219            let sender = ensure_signed(origin)?;1220            1221            Self::collection_exists(collection_id)?;1222            Self::token_exists(collection_id, item_id, &sender)?;12231224            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12251226            // Modify permissions check1227            let target_collection = <Collection<T>>::get(collection_id);1228            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1229                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1230                Error::<T>::NoPermission);12311232            match target_collection.mode1233            {1234                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1235                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1236                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1237                _ => fail!(Error::<T>::UnexpectedCollectionType)1238            };12391240            Ok(())1241        }1242 1243        /// Set schema standard1244        /// ImageURL1245        /// Unique1246        /// 1247        /// # Permissions1248        /// 1249        /// * Collection Owner1250        /// * Collection Admin1251        /// 1252        /// # Arguments1253        /// 1254        /// * collection_id.1255        /// 1256        /// * schema: SchemaVersion: enum1257        #[weight = T::WeightInfo::set_schema_version()]1258        pub fn set_schema_version(1259            origin,1260            collection_id: CollectionId,1261            version: SchemaVersion1262        ) -> DispatchResult {1263            let sender = ensure_signed(origin)?;1264            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1265            let mut target_collection = <Collection<T>>::get(collection_id);1266            target_collection.schema_version = version;1267            <Collection<T>>::insert(collection_id, target_collection);12681269            Ok(())1270        }12711272        /// Set off-chain data schema.1273        /// 1274        /// # Permissions1275        /// 1276        /// * Collection Owner1277        /// * Collection Admin1278        /// 1279        /// # Arguments1280        /// 1281        /// * collection_id.1282        /// 1283        /// * schema: String representing the offchain data schema.1284        #[weight = T::WeightInfo::set_offchain_schema()]1285        pub fn set_offchain_schema(1286            origin,1287            collection_id: CollectionId,1288            schema: Vec<u8>1289        ) -> DispatchResult {1290            let sender = ensure_signed(origin)?;1291            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12921293            // check schema limit1294            ensure!(schema.len() as u32 > ChainLimit::get().offchain_schema_limit, "");12951296            let mut target_collection = <Collection<T>>::get(collection_id);1297            target_collection.offchain_schema = schema;1298            <Collection<T>>::insert(collection_id, target_collection);12991300            Ok(())1301        }13021303        /// Set const on-chain data schema.1304        /// 1305        /// # Permissions1306        /// 1307        /// * Collection Owner1308        /// * Collection Admin1309        /// 1310        /// # Arguments1311        /// 1312        /// * collection_id.1313        /// 1314        /// * schema: String representing the const on-chain data schema.1315        #[weight = T::WeightInfo::set_const_on_chain_schema()]1316        pub fn set_const_on_chain_schema (1317            origin,1318            collection_id: CollectionId,1319            schema: Vec<u8>1320        ) -> DispatchResult {1321            let sender = ensure_signed(origin)?;1322            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13231324            // check schema limit1325            ensure!(schema.len() as u32 > ChainLimit::get().const_on_chain_schema_limit, "");13261327            let mut target_collection = <Collection<T>>::get(collection_id);1328            target_collection.const_on_chain_schema = schema;1329            <Collection<T>>::insert(collection_id, target_collection);13301331            Ok(())1332        }13331334        /// Set variable on-chain data schema.1335        /// 1336        /// # Permissions1337        /// 1338        /// * Collection Owner1339        /// * Collection Admin1340        /// 1341        /// # Arguments1342        /// 1343        /// * collection_id.1344        /// 1345        /// * schema: String representing the variable on-chain data schema.1346        #[weight = T::WeightInfo::set_const_on_chain_schema()]1347        pub fn set_variable_on_chain_schema (1348            origin,1349            collection_id: CollectionId,1350            schema: Vec<u8>1351        ) -> DispatchResult {1352            let sender = ensure_signed(origin)?;1353            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13541355            // check schema limit1356            ensure!(schema.len() as u32 > ChainLimit::get().variable_on_chain_schema_limit, "");13571358            let mut target_collection = <Collection<T>>::get(collection_id);1359            target_collection.variable_on_chain_schema = schema;1360            <Collection<T>>::insert(collection_id, target_collection);13611362            Ok(())1363        }13641365        // Sudo permissions function1366        #[weight = T::WeightInfo::set_chain_limits()]1367        pub fn set_chain_limits(1368            origin,1369            limits: ChainLimits1370        ) -> DispatchResult {13711372            #[cfg(not(feature = "runtime-benchmarks"))]1373            ensure_root(origin)?;13741375            <ChainLimit>::put(limits);1376            Ok(())1377        }13781379        /// Enable smart contract self-sponsoring.1380        /// 1381        /// # Permissions1382        /// 1383        /// * Contract Owner1384        /// 1385        /// # Arguments1386        /// 1387        /// * contract address1388        /// * enable flag1389        /// 1390        #[weight = T::WeightInfo::enable_contract_sponsoring()]1391        pub fn enable_contract_sponsoring(1392            origin,1393            contract_address: T::AccountId,1394            enable: bool1395        ) -> DispatchResult {13961397            let sender = ensure_signed(origin)?;13981399            #[cfg(feature = "runtime-benchmarks")]1400            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14011402            Self::ensure_contract_owned(sender, &contract_address)?;14031404            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1405            Ok(())1406        }14071408        /// Set the rate limit for contract sponsoring to specified number of blocks.1409        /// 1410        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1411        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1412        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1413        /// from contract endowment if there are at least B blocks between such transactions. 1414        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1415        /// 1416        /// # Permissions1417        /// 1418        /// * Contract Owner1419        /// 1420        /// # Arguments1421        /// 1422        /// -`contract_address`: Address of the contract to sponsor1423        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1424        /// 1425        #[weight = T::WeightInfo::set_contract_sponsoring_rate_limit()]1426        pub fn set_contract_sponsoring_rate_limit(1427            origin,1428            contract_address: T::AccountId,1429            rate_limit: T::BlockNumber1430        ) -> DispatchResult {1431            let sender = ensure_signed(origin)?;14321433            #[cfg(feature = "runtime-benchmarks")]1434            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14351436            Self::ensure_contract_owned(sender, &contract_address)?;1437            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1438            Ok(())1439        }14401441        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1442        /// 1443        /// # Permissions1444        /// 1445        /// * Address that deployed smart contract.1446        /// 1447        /// # Arguments1448        /// 1449        /// -`contract_address`: Address of the contract.1450        /// 1451        /// - `enable`: .  1452        #[weight = T::WeightInfo::toggle_contract_white_list()]1453        pub fn toggle_contract_white_list(1454            origin,1455            contract_address: T::AccountId,1456            enable: bool1457        ) -> DispatchResult {1458            let sender = ensure_signed(origin)?;14591460            #[cfg(feature = "runtime-benchmarks")]1461            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14621463            Self::ensure_contract_owned(sender, &contract_address)?;1464            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1465            Ok(())1466        }1467        1468        /// Add an address to smart contract white list.1469        /// 1470        /// # Permissions1471        /// 1472        /// * Address that deployed smart contract.1473        /// 1474        /// # Arguments1475        /// 1476        /// -`contract_address`: Address of the contract.1477        ///1478        /// -`account_address`: Address to add.1479        #[weight = T::WeightInfo::add_to_contract_white_list()]1480        pub fn add_to_contract_white_list(1481            origin,1482            contract_address: T::AccountId,1483            account_address: T::AccountId1484        ) -> DispatchResult {1485            let sender = ensure_signed(origin)?;14861487            #[cfg(feature = "runtime-benchmarks")]1488            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1489            1490            Self::ensure_contract_owned(sender, &contract_address)?;      1491            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1492            Ok(())1493        }14941495        /// Remove an address from smart contract white list.1496        /// 1497        /// # Permissions1498        /// 1499        /// * Address that deployed smart contract.1500        /// 1501        /// # Arguments1502        /// 1503        /// -`contract_address`: Address of the contract.1504        ///1505        /// -`account_address`: Address to remove.1506        #[weight = T::WeightInfo::remove_from_contract_white_list()]1507        pub fn remove_from_contract_white_list(1508            origin,1509            contract_address: T::AccountId,1510            account_address: T::AccountId1511        ) -> DispatchResult {1512            let sender = ensure_signed(origin)?;15131514            #[cfg(feature = "runtime-benchmarks")]1515            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15161517            Self::ensure_contract_owned(sender, &contract_address)?;1518            <ContractWhiteList<T>>::remove(contract_address, account_address);1519            Ok(())1520        }15211522        #[weight = T::WeightInfo::set_collection_limits()]1523        pub fn set_collection_limits(1524            origin,1525            collection_id: u32,1526            limits: CollectionLimits,1527        ) -> DispatchResult {1528            let sender = ensure_signed(origin)?;1529            Self::check_owner_permissions(collection_id, sender.clone())?;1530            let mut target_collection = <Collection<T>>::get(collection_id);1531            let chain_limits = ChainLimit::get();1532            let climits = target_collection.limits;15331534            // collection bounds1535            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1536                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1537                Error::<T>::CollectionLimitBoundsExceeded);15381539            // token_limit   check  prev1540            ensure!(climits.token_limit > limits.token_limit && 1541                limits.token_limit <= chain_limits.account_token_ownership_limit, 1542                Error::<T>::AccountTokenLimitExceeded);15431544            target_collection.limits = limits;1545            <Collection<T>>::insert(collection_id, target_collection);15461547            Ok(())1548        } 1549    }1550}15511552impl<T: Trait> Module<T> {15531554    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15551556        // check token limit and account token limit1557        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1558        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1559        1560        Ok(())1561    }15621563    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15641565        // check token limit and account token limit1566        let total_items: u32 = ItemListIndex::get(collection_id);1567        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1568        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1569        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15701571        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1572            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1573            Self::check_white_list(collection_id, owner)?;1574            Self::check_white_list(collection_id, sender)?;1575        }15761577        Ok(())1578    }15791580    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1581        match target_collection.mode1582        {1583            CollectionMode::NFT => {1584                if let CreateItemData::NFT(data) = data {1585                    // check sizes1586                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1587                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1588                } else {1589                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1590                }1591            },1592            CollectionMode::Fungible(_) => {1593                if let CreateItemData::Fungible(_) = data {1594                } else {1595                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1596                }1597            },1598            CollectionMode::ReFungible(_) => {1599                if let CreateItemData::ReFungible(data) = data {16001601                    // check sizes1602                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1603                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1604                } else {1605                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1606                }1607            },1608            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1609        };16101611        Ok(())1612    }16131614    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1615        match data1616        {1617            CreateItemData::NFT(data) => {1618                let item = NftItemType {1619                    owner,1620                    const_data: data.const_data,1621                    variable_data: data.variable_data1622                };16231624                Self::add_nft_item(collection_id, item)?;1625            },1626            CreateItemData::Fungible(data) => {1627                Self::add_fungible_item(collection_id, &owner, data.value)?;1628            },1629            CreateItemData::ReFungible(data) => {1630                let mut owner_list = Vec::new();1631                let value = (10 as u128).pow(collection.decimal_points as u32);1632                owner_list.push(Ownership {owner: owner.clone(), fraction: value});16331634                let item = ReFungibleItemType {1635                    owner: owner_list,1636                    const_data: data.const_data,1637                    variable_data: data.variable_data1638                };16391640                Self::add_refungible_item(collection_id, item)?;1641            }1642        };16431644        // call event1645        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16461647        Ok(())1648    }16491650    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {16511652        // Does new owner already have an account?1653        let mut balance: u128 = 0;1654        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1655            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1656        } 16571658        // Mint 1659        let item = FungibleItemType {1660            value: balance + value1661        };1662        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);16631664        // Update balance1665        let new_balance = <Balance<T>>::get(collection_id, owner)1666            .checked_add(value)1667            .ok_or(Error::<T>::NumOverflow)?;1668        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);16691670        Ok(())1671    }16721673    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1674        let current_index = <ItemListIndex>::get(collection_id)1675            .checked_add(1)1676            .ok_or(Error::<T>::NumOverflow)?;1677        let itemcopy = item.clone();16781679        let value = item.owner.first().unwrap().fraction;1680        let owner = item.owner.first().unwrap().owner.clone();16811682        Self::add_token_index(collection_id, current_index, &owner)?;16831684        <ItemListIndex>::insert(collection_id, current_index);1685        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);16861687        // Update balance1688        let new_balance = <Balance<T>>::get(collection_id, &owner)1689            .checked_add(value)1690            .ok_or(Error::<T>::NumOverflow)?;1691        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);16921693        Ok(())1694    }16951696    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1697        let current_index = <ItemListIndex>::get(collection_id)1698            .checked_add(1)1699            .ok_or(Error::<T>::NumOverflow)?;17001701        let item_owner = item.owner.clone();1702        Self::add_token_index(collection_id, current_index, &item.owner)?;17031704        <ItemListIndex>::insert(collection_id, current_index);1705        <NftItemList<T>>::insert(collection_id, current_index, item);17061707        // Update balance1708        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1709            .checked_add(1)1710            .ok_or(Error::<T>::NumOverflow)?;1711        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);17121713        Ok(())1714    }17151716    fn burn_refungible_item(1717        collection_id: CollectionId,1718        item_id: TokenId,1719        owner: &T::AccountId,1720    ) -> DispatchResult {1721        ensure!(1722            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1723            Error::<T>::TokenNotFound1724        );1725        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1726        let rft_balance = token1727            .owner1728            .iter()1729            .filter(|&i| i.owner == *owner)1730            .next()1731            .unwrap();1732        Self::remove_token_index(collection_id, item_id, owner)?;17331734        // update balance1735        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1736            .checked_sub(rft_balance.fraction)1737            .ok_or(Error::<T>::NumOverflow)?;1738        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);17391740        // Re-create owners list with sender removed1741        let index = token1742            .owner1743            .iter()1744            .position(|i| i.owner == *owner)1745            .unwrap();1746        token.owner.remove(index);1747        let owner_count = token.owner.len();17481749        // Burn the token completely if this was the last (only) owner1750        if owner_count == 0 {1751            <ReFungibleItemList<T>>::remove(collection_id, item_id);1752        }1753        else {1754            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1755        }17561757        Ok(())1758    }17591760    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1761        ensure!(1762            <NftItemList<T>>::contains_key(collection_id, item_id),1763            Error::<T>::TokenNotFound1764        );1765        let item = <NftItemList<T>>::get(collection_id, item_id);1766        Self::remove_token_index(collection_id, item_id, &item.owner)?;17671768        // update balance1769        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1770            .checked_sub(1)1771            .ok_or(Error::<T>::NumOverflow)?;1772        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1773        <NftItemList<T>>::remove(collection_id, item_id);17741775        Ok(())1776    }17771778    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1779        ensure!(1780            <FungibleItemList<T>>::contains_key(collection_id, owner),1781            Error::<T>::TokenNotFound1782        );1783        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1784        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);17851786        // update balance1787        let new_balance = <Balance<T>>::get(collection_id, owner)1788            .checked_sub(value)1789            .ok_or(Error::<T>::NumOverflow)?;1790        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17911792        if balance.value - value > 0 {1793            balance.value -= value;1794            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1795        }1796        else {1797            <FungibleItemList<T>>::remove(collection_id, owner);1798        }17991800        Ok(())1801    }18021803    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1804        ensure!(1805            <Collection<T>>::contains_key(collection_id),1806            Error::<T>::CollectionNotFound1807        );1808        Ok(())1809    }18101811    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1812        Self::collection_exists(collection_id)?;18131814        let target_collection = <Collection<T>>::get(collection_id);1815        ensure!(1816            subject == target_collection.owner,1817            Error::<T>::NoPermission1818        );18191820        Ok(())1821    }18221823    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1824        let target_collection = <Collection<T>>::get(collection_id);1825        let mut result: bool = subject == target_collection.owner;1826        let exists = <AdminList<T>>::contains_key(collection_id);18271828        if !result & exists {1829            if <AdminList<T>>::get(collection_id).contains(&subject) {1830                result = true1831            }1832        }18331834        result1835    }18361837    fn check_owner_or_admin_permissions(1838        collection_id: CollectionId,1839        subject: T::AccountId,1840    ) -> DispatchResult {1841        Self::collection_exists(collection_id)?;1842        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());18431844        ensure!(1845            result,1846            Error::<T>::NoPermission1847        );1848        Ok(())1849    }18501851    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1852        let target_collection = <Collection<T>>::get(collection_id);18531854        match target_collection.mode {1855            CollectionMode::NFT => {1856                <NftItemList<T>>::get(collection_id, item_id).owner == subject1857            }1858            CollectionMode::Fungible(_) => {1859                <FungibleItemList<T>>::contains_key(collection_id, &subject)1860            }1861            CollectionMode::ReFungible(_) => {1862                <ReFungibleItemList<T>>::get(collection_id, item_id)1863                    .owner1864                    .iter()1865                    .any(|i| i.owner == subject)1866            }1867            CollectionMode::Invalid => false,1868        }1869    }18701871    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1872        let mes = Error::<T>::AddresNotInWhiteList;1873        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);18741875        Ok(())1876    }18771878    /// Check if token exists. In case of Fungible, check if there is an entry for 1879    /// the owner in fungible balances double map1880    fn token_exists(1881        collection_id: CollectionId,1882        item_id: TokenId,1883        owner: &T::AccountId1884    ) -> DispatchResult {1885        let target_collection = <Collection<T>>::get(collection_id);1886        let exists = match target_collection.mode1887        {1888            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1889            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),1890            CollectionMode::ReFungible(_)  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1891            _ => false1892        };18931894        ensure!(exists == true, Error::<T>::TokenNotFound);1895        Ok(())1896    }18971898    fn transfer_fungible(1899        collection_id: CollectionId,1900        value: u128,1901        owner: &T::AccountId,1902        recipient: &T::AccountId,1903    ) -> DispatchResult {1904        Self::token_exists(collection_id, 0, owner)?;19051906        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1907        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19081909        // Send balance to recipient (updates balanceOf of recipient)1910        Self::add_fungible_item(collection_id, recipient, value)?;19111912        // update balanceOf of sender1913        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);19141915        // Reduce or remove sender1916        if balance.value == value {1917            <FungibleItemList<T>>::remove(collection_id, owner);1918        }1919        else {1920            balance.value -= value;1921            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1922        }19231924        Ok(())1925    }19261927    fn transfer_refungible(1928        collection_id: CollectionId,1929        item_id: TokenId,1930        value: u128,1931        owner: T::AccountId,1932        new_owner: T::AccountId,1933    ) -> DispatchResult {1934        Self::token_exists(collection_id, item_id, &owner)?;19351936        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1937        let item = full_item1938            .owner1939            .iter()1940            .filter(|i| i.owner == owner)1941            .next()1942            .ok_or(Error::<T>::NumOverflow)?;1943        let amount = item.fraction;19441945        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19461947        // update balance1948        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1949            .checked_sub(value)1950            .ok_or(Error::<T>::NumOverflow)?;1951        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19521953        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1954            .checked_add(value)1955            .ok_or(Error::<T>::NumOverflow)?;1956        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);19571958        let old_owner = item.owner.clone();1959        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19601961        // transfer1962        if amount == value && !new_owner_has_account {1963            // change owner1964            // new owner do not have account1965            let mut new_full_item = full_item.clone();1966            new_full_item1967                .owner1968                .iter_mut()1969                .find(|i| i.owner == owner)1970                .unwrap()1971                .owner = new_owner.clone();1972            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19731974            // update index collection1975            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;1976        } else {1977            let mut new_full_item = full_item.clone();1978            new_full_item1979                .owner1980                .iter_mut()1981                .find(|i| i.owner == owner)1982                .unwrap()1983                .fraction -= value;19841985            // separate amount1986            if new_owner_has_account {1987                // new owner has account1988                new_full_item1989                    .owner1990                    .iter_mut()1991                    .find(|i| i.owner == new_owner)1992                    .unwrap()1993                    .fraction += value;1994            } else {1995                // new owner do not have account1996                new_full_item.owner.push(Ownership {1997                    owner: new_owner.clone(),1998                    fraction: value,1999                });2000                Self::add_token_index(collection_id, item_id, &new_owner)?;2001            }20022003            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2004        }20052006        Ok(())2007    }20082009    fn transfer_nft(2010        collection_id: CollectionId,2011        item_id: TokenId,2012        sender: T::AccountId,2013        new_owner: T::AccountId,2014    ) -> DispatchResult {2015        Self::token_exists(collection_id, item_id, &sender)?;20162017        let mut item = <NftItemList<T>>::get(collection_id, item_id);20182019        ensure!(2020            sender == item.owner,2021            Error::<T>::MustBeTokenOwner2022        );20232024        // update balance2025        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2026            .checked_sub(1)2027            .ok_or(Error::<T>::NumOverflow)?;2028        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20292030        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2031            .checked_add(1)2032            .ok_or(Error::<T>::NumOverflow)?;2033        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20342035        // change owner2036        let old_owner = item.owner.clone();2037        item.owner = new_owner.clone();2038        <NftItemList<T>>::insert(collection_id, item_id, item);20392040        // update index collection2041        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;20422043        Ok(())2044    }2045    2046    fn set_re_fungible_variable_data(2047        collection_id: CollectionId,2048        item_id: TokenId,2049        data: Vec<u8>2050    ) -> DispatchResult {2051        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20522053        item.variable_data = data;20542055        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20562057        Ok(())2058    }20592060    fn set_nft_variable_data(2061        collection_id: CollectionId,2062        item_id: TokenId,2063        data: Vec<u8>2064    ) -> DispatchResult {2065        let mut item = <NftItemList<T>>::get(collection_id, item_id);2066        2067        item.variable_data = data;20682069        <NftItemList<T>>::insert(collection_id, item_id, item);2070        2071        Ok(())2072    }20732074    fn init_collection(item: &CollectionType<T::AccountId>) {2075        // check params2076        assert!(2077            item.decimal_points <= MAX_DECIMAL_POINTS,2078            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2079        );2080        assert!(2081            item.name.len() <= 64,2082            "Collection name can not be longer than 63 char"2083        );2084        assert!(2085            item.name.len() <= 256,2086            "Collection description can not be longer than 255 char"2087        );2088        assert!(2089            item.token_prefix.len() <= 16,2090            "Token prefix can not be longer than 15 char"2091        );20922093        // Generate next collection ID2094        let next_id = CreatedCollectionCount::get()2095            .checked_add(1)2096            .unwrap();20972098        CreatedCollectionCount::put(next_id);2099    }21002101    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2102        let current_index = <ItemListIndex>::get(collection_id)2103            .checked_add(1)2104            .unwrap();21052106        let item_owner = item.owner.clone();2107        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21082109        <ItemListIndex>::insert(collection_id, current_index);21102111        // Update balance2112        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2113            .checked_add(1)2114            .unwrap();2115        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2116    }21172118    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2119        let current_index = <ItemListIndex>::get(collection_id)2120            .checked_add(1)2121            .unwrap();21222123        Self::add_token_index(collection_id, current_index, owner).unwrap();21242125        <ItemListIndex>::insert(collection_id, current_index);21262127        // Update balance2128        let new_balance = <Balance<T>>::get(collection_id, owner)2129            .checked_add(item.value)2130            .unwrap();2131        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2132    }21332134    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2135        let current_index = <ItemListIndex>::get(collection_id)2136            .checked_add(1)2137            .unwrap();21382139        let value = item.owner.first().unwrap().fraction;2140        let owner = item.owner.first().unwrap().owner.clone();21412142        Self::add_token_index(collection_id, current_index, &owner).unwrap();21432144        <ItemListIndex>::insert(collection_id, current_index);21452146        // Update balance2147        let new_balance = <Balance<T>>::get(collection_id, &owner)2148            .checked_add(value)2149            .unwrap();2150        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2151    }21522153    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {21542155        // add to account limit2156        if <AccountItemCount<T>>::contains_key(owner) {21572158            // bound Owned tokens by a single address2159            let count = <AccountItemCount<T>>::get(owner);2160            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21612162            <AccountItemCount<T>>::insert(owner.clone(), count2163                .checked_add(1)2164                .ok_or(Error::<T>::NumOverflow)?);2165        }2166        else {2167            <AccountItemCount<T>>::insert(owner.clone(), 1);2168        }21692170        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2171        if list_exists {2172            let mut list = <AddressTokens<T>>::get(collection_id, owner);2173            let item_contains = list.contains(&item_index.clone());21742175            if !item_contains {2176                list.push(item_index.clone());2177            }21782179            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2180        } else {2181            let mut itm = Vec::new();2182            itm.push(item_index.clone());2183            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2184        }21852186        Ok(())2187    }21882189    fn remove_token_index(2190        collection_id: CollectionId,2191        item_index: TokenId,2192        owner: &T::AccountId,2193    ) -> DispatchResult {21942195        // update counter2196        <AccountItemCount<T>>::insert(owner.clone(), 2197            <AccountItemCount<T>>::get(owner)2198            .checked_sub(1)2199            .ok_or(Error::<T>::NumOverflow)?);220022012202        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2203        if list_exists {2204            let mut list = <AddressTokens<T>>::get(collection_id, owner);2205            let item_contains = list.contains(&item_index.clone());22062207            if item_contains {2208                list.retain(|&item| item != item_index);2209                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2210            }2211        }22122213        Ok(())2214    }22152216    fn move_token_index(2217        collection_id: CollectionId,2218        item_index: TokenId,2219        old_owner: &T::AccountId,2220        new_owner: &T::AccountId,2221    ) -> DispatchResult {2222        Self::remove_token_index(collection_id, item_index, old_owner)?;2223        Self::add_token_index(collection_id, item_index, new_owner)?;22242225        Ok(())2226    }2227    2228    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2229        if <ContractOwner<T>>::contains_key(contract.clone()) {2230            let owner = <ContractOwner<T>>::get(contract);2231            ensure!(account == owner, Error::<T>::NoPermission);2232        } else {2233            fail!(Error::<T>::NoPermission);2234        }22352236        Ok(())2237    }2238}22392240////////////////////////////////////////////////////////////////////////////////////////////////////2241// Economic models2242// #region22432244/// Fee multiplier.2245pub type Multiplier = FixedU128;22462247type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2248    <T as system::Trait>::AccountId,2249>>::Balance;2250type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2251    <T as system::Trait>::AccountId,2252>>::NegativeImbalance;22532254/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2255/// in the queue.2256#[derive(Encode, Decode, Clone, Eq, PartialEq)]2257pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2258    #[codec(compact)] BalanceOf<T>2259);22602261impl<T: Trait + Send + Sync> sp_std::fmt::Debug2262    for ChargeTransactionPayment<T>2263{2264    #[cfg(feature = "std")]2265    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2266        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2267    }2268    #[cfg(not(feature = "std"))]2269    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2270        Ok(())2271    }2272}22732274impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2275where2276    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2277    BalanceOf<T>: Send + Sync + FixedPointOperand,2278{2279    /// utility constructor. Used only in client/factory code.2280    pub fn from(fee: BalanceOf<T>) -> Self {2281        Self(fee)2282    }22832284    pub fn traditional_fee(2285        len: usize,2286        info: &DispatchInfoOf<T::Call>,2287        tip: BalanceOf<T>,2288    ) -> BalanceOf<T>2289    where2290        T::Call: Dispatchable<Info = DispatchInfo>,2291    {2292        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2293    }22942295	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2296		let weight_saturation = T::MaximumBlockWeight::get() / info.weight.max(1);2297		let len_saturation = T::MaximumBlockLength::get() as u64 / (len as u64).max(1);2298		let coefficient: BalanceOf<T> = weight_saturation.min(len_saturation).saturated_into::<BalanceOf<T>>();2299		final_fee.saturating_mul(coefficient).saturated_into::<TransactionPriority>()2300	}23012302    fn withdraw_fee(2303        &self,2304        who: &T::AccountId,2305        call: &T::Call,2306        info: &DispatchInfoOf<T::Call>,2307        len: usize,2308    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2309        let tip = self.0;23102311        // Set fee based on call type. Creating collection costs 1 Unique.2312        // All other transactions have traditional fees so far2313        // let fee = match call.is_sub_type() {2314        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2315        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2316        //                                                 // _ => <BalanceOf<T>>::from(100)2317        // };2318        let fee = Self::traditional_fee(len, info, tip);23192320        // Only mess with balances if fee is not zero.2321        if fee.is_zero() {2322            return Ok((fee, None));2323        }23242325        // Determine who is paying transaction fee based on ecnomic model2326        // Parse call to extract collection ID and access collection sponsor2327        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2328            Some(Call::create_item(collection_id, _owner, _properties)) => {23292330                // sponsor timeout2331                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;23322333                let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;2334                let mut sponsored = true;2335                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2336                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2337                    let limit_time = last_tx_block + limit.into();2338                    if block_number <= limit_time {2339                        sponsored = false;2340                    }2341                }2342                if sponsored {2343                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2344                }23452346                // check free create limit2347                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2348                   (<Collection<T>>::get(collection_id).sponsor_confirmed) &&2349                   (sponsored)2350                {2351                    <Collection<T>>::get(collection_id).sponsor2352                } else {2353                    T::AccountId::default()2354                }2355            }2356            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2357                2358                let mut sponsor_transfer = false;2359                if <Collection<T>>::get(collection_id).sponsor_confirmed {23602361                    let collection_limits = <Collection<T>>::get(collection_id).limits;2362                    let collection_mode = <Collection<T>>::get(collection_id).mode;2363    2364                    // sponsor timeout2365                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2366                    sponsor_transfer = match collection_mode {2367                        CollectionMode::NFT => {2368    2369                            // get correct limit2370                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2371                                collection_limits.sponsor_transfer_timeout2372                            } else {2373                                ChainLimit::get().nft_sponsor_transfer_timeout2374                            };2375    2376                            let mut sponsored = true;2377                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2378                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2379                                let limit_time = last_tx_block + limit.into();2380                                if block_number <= limit_time {2381                                    sponsored = false;2382                                }2383                            }2384                            if sponsored {2385                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2386                            }23872388                            sponsored2389                        }2390                        CollectionMode::Fungible(_) => {2391    2392                            // get correct limit2393                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2394                                collection_limits.sponsor_transfer_timeout2395                            } else {2396                                ChainLimit::get().fungible_sponsor_transfer_timeout2397                            };2398    2399                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2400                            let mut sponsored = true;2401                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2402                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2403                                let limit_time = last_tx_block + limit.into();2404                                if block_number <= limit_time {2405                                    sponsored = false;2406                                }2407                            }2408                            if sponsored {2409                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2410                            }24112412                            sponsored2413                        }2414                        CollectionMode::ReFungible(_) => {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().refungible_sponsor_transfer_timeout2421                            };2422    2423                            let mut sponsored = true;2424                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2425                                let last_tx_block = <ReFungibleTransferBasket<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                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2433                            }24342435                            sponsored2436                        }2437                        _ => {2438                            false2439                        },2440                    };2441                }24422443                if !sponsor_transfer {2444                    T::AccountId::default()2445                } else {2446                    <Collection<T>>::get(collection_id).sponsor2447                }2448            }24492450            _ => T::AccountId::default(),2451        };24522453        // Sponsor smart contracts2454        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {24552456            // On instantiation: set the contract owner2457            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {24582459                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2460                    code_hash,2461                    &data,2462                    &who,2463                );2464                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());24652466                T::AccountId::default()2467            },24682469            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2470            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {24712472                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());24732474                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2475                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2476                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2477                  2478                if !owned_contract && white_list_enabled {2479                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2480                        return Err(InvalidTransaction::Call.into());2481                    }2482                }24832484                let mut sponsor_transfer = false;2485                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2486                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2487                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2488                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2489                    let limit_time = last_tx_block + rate_limit;24902491                    if block_number >= limit_time {2492                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2493                        sponsor_transfer = true;2494                    }2495                } else {2496                    sponsor_transfer = false;2497                }2498               2499                2500                let mut sp = T::AccountId::default();2501                if sponsor_transfer {2502                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2503                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2504                            sp = called_contract;2505                        }2506                    }2507                }25082509                sp2510            },25112512            _ => sponsor,2513        };25142515        let mut who_pays_fee: T::AccountId = sponsor.clone();2516        if sponsor == T::AccountId::default() {2517            who_pays_fee = who.clone();2518        }25192520        match <T as transaction_payment::Trait>::Currency::withdraw(2521            &who_pays_fee,2522            fee,2523            if tip.is_zero() {2524                WithdrawReason::TransactionPayment.into()2525            } else {2526                WithdrawReason::TransactionPayment | WithdrawReason::Tip2527            },2528            ExistenceRequirement::KeepAlive,2529        ) {2530            Ok(imbalance) => Ok((fee, Some(imbalance))),2531            Err(_) => Err(InvalidTransaction::Payment.into()),2532        }2533    }2534}253525362537impl<T: Trait + Send + Sync> SignedExtension2538    for ChargeTransactionPayment<T>2539where2540    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2541    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2542{2543    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2544    type AccountId = T::AccountId;2545    type Call = T::Call;2546    type AdditionalSigned = ();2547    type Pre = (2548        BalanceOf<T>,2549        Self::AccountId,2550        Option<NegativeImbalanceOf<T>>,2551        BalanceOf<T>,2552    );2553    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2554        Ok(())2555    }25562557    fn validate(2558        &self,2559        who: &Self::AccountId,2560        call: &Self::Call,2561        info: &DispatchInfoOf<Self::Call>,2562        len: usize,2563    ) -> TransactionValidity {2564		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2565		Ok(ValidTransaction {2566			priority: Self::get_priority(len, info, fee),2567			..Default::default()2568		})2569    }25702571    fn pre_dispatch(2572        self,2573        who: &Self::AccountId,2574        call: &Self::Call,2575        info: &DispatchInfoOf<Self::Call>,2576        len: usize,2577    ) -> Result<Self::Pre, TransactionValidityError> {2578        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2579        Ok((self.0, who.clone(), imbalance, fee))2580    }25812582    fn post_dispatch(2583        pre: Self::Pre,2584        info: &DispatchInfoOf<Self::Call>,2585        post_info: &PostDispatchInfoOf<Self::Call>,2586        len: usize,2587        _result: &DispatchResult,2588    ) -> Result<(), TransactionValidityError> {2589        let (tip, who, imbalance, fee) = pre;2590        if let Some(payed) = imbalance {2591            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2592                len as u32, info, post_info, tip,2593            );2594            let refund = fee.saturating_sub(actual_fee);2595            let actual_payment =2596                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2597                    &who, refund,2598                ) {2599                    Ok(refund_imbalance) => {2600                        // The refund cannot be larger than the up front payed max weight.2601                        // `PostDispatchInfo::calc_unspent` guards against such a case.2602                        match payed.offset(refund_imbalance) {2603                            Ok(actual_payment) => actual_payment,2604                            Err(_) => return Err(InvalidTransaction::Payment.into()),2605                        }2606                    }2607                    // We do not recreate the account using the refund. The up front payment2608                    // is gone in that case.2609                    Err(_) => payed,2610                };2611            let imbalances = actual_payment.split(tip);2612            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2613                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2614            );2615        }2616        Ok(())2617    }2618}26192620// #endregion
modifiedtests/src/addToWhiteList.test.tsdiffbeforeafterboth
--- a/tests/src/addToWhiteList.test.ts
+++ b/tests/src/addToWhiteList.test.ts
@@ -18,7 +18,7 @@
 let Alice: IKeyringPair;
 let Bob: IKeyringPair;
 
-describe.only('Integration Test ext. addToWhiteList()', () => {
+describe('Integration Test ext. addToWhiteList()', () => {
 
   before(async () => {
     await usingApi(async (api) => {
@@ -41,7 +41,7 @@
   });
 });
 
-describe.only('Negative Integration Test ext. addToWhiteList()', () => {
+describe('Negative Integration Test ext. addToWhiteList()', () => {
 
   it('White list an address in the collection that does not exist', async () => {
     await usingApi(async (api) => {