git.delta.rocks / unique-network / refs/commits / 5d12903f3ba5

difftreelog

source

pallets/nft/src/lib.rs94.9 KiBsourcehistory
1#![recursion_limit = "1024"]23#![cfg_attr(not(feature = "std"), no_std)]45#[cfg(feature = "std")]6pub use std::*;78#[cfg(feature = "std")]9pub use serde::*;1011use codec::{Decode, Encode};12pub use frame_support::{13    construct_runtime, decl_event, decl_module, decl_storage, decl_error,14    dispatch::DispatchResult,15    ensure, fail, parameter_types,16    traits::{17        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,18        Randomness, WithdrawReason,19    },20    weights::{21        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},22        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,23        WeightToFeePolynomial,24    },25    IsSubType, StorageValue,26};2728use frame_system::{self as system, ensure_signed, ensure_root};29use sp_runtime::sp_std::prelude::Vec;30use sp_runtime::{31    traits::{32        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,33    },34    transaction_validity::{35        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,36    },37    FixedPointOperand, FixedU128,38};39use pallet_contracts::ContractAddressFor;40use sp_runtime::traits::StaticLookup;4142#[cfg(test)]43mod mock;4445#[cfg(test)]46mod tests;4748mod default_weights;4950pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;51pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;52pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;5354// Structs55// #region5657pub type CollectionId = u32;58pub type TokenId = u32;59pub type DecimalPoints = u8;6061#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]62#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]63pub enum CollectionMode {64    Invalid,65    NFT,66    // decimal points67    Fungible(DecimalPoints),68    // decimal points69    ReFungible(DecimalPoints),70}7172impl Into<u8> for CollectionMode {73    fn into(self) -> u8 {74        match self {75            CollectionMode::Invalid => 0,76            CollectionMode::NFT => 1,77            CollectionMode::Fungible(_) => 2,78            CollectionMode::ReFungible(_) => 3,79        }80    }81}8283#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]84#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]85pub enum AccessMode {86    Normal,87    WhiteList,88}89impl Default for AccessMode {90    fn default() -> Self {91        Self::Normal92    }93}9495impl Default for CollectionMode {96    fn default() -> Self {97        Self::Invalid98    }99}100101#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]102#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]103pub enum SchemaVersion {104    ImageURL,105    Unique,106}107impl Default for SchemaVersion {108    fn default() -> Self {109        Self::ImageURL110    }111}112113#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]114#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]115pub struct Ownership<AccountId> {116    pub owner: AccountId,117    pub fraction: u128,118}119120#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]121#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]122pub struct CollectionType<AccountId> {123    pub owner: AccountId,124    pub mode: CollectionMode,125    pub access: AccessMode,126    pub decimal_points: DecimalPoints,127    pub name: Vec<u16>,        // 64 include null escape char128    pub description: Vec<u16>, // 256 include null escape char129    pub token_prefix: Vec<u8>, // 16 include null escape char130    pub mint_mode: bool,131    pub offchain_schema: Vec<u8>,132    pub schema_version: SchemaVersion,133    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender134    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship135    pub limits: CollectionLimits, // Collection private restrictions 136    pub variable_on_chain_schema: Vec<u8>, //137    pub const_on_chain_schema: Vec<u8>, //138}139140#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]141#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]142pub struct NftItemType<AccountId> {143    pub collection: CollectionId,144    pub owner: AccountId,145    pub const_data: Vec<u8>,146    pub variable_data: Vec<u8>,147}148149#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]150#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]151pub struct FungibleItemType<AccountId> {152    pub collection: CollectionId,153    pub owner: AccountId,154    pub value: u128,155}156157#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]158#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]159pub struct ReFungibleItemType<AccountId> {160    pub collection: CollectionId,161    pub owner: Vec<Ownership<AccountId>>,162    pub const_data: Vec<u8>,163    pub variable_data: Vec<u8>,164}165166#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]167#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]168pub struct ApprovePermissions<AccountId> {169    pub approved: AccountId,170    pub amount: u128,171}172173#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]174#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]175pub struct VestingItem<AccountId, Moment> {176    pub sender: AccountId,177    pub recipient: AccountId,178    pub collection_id: CollectionId,179    pub item_id: TokenId,180    pub amount: u64,181    pub vesting_date: Moment,182}183184#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]185#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]186pub struct BasketItem<AccountId, BlockNumber> {187    pub address: AccountId,188    pub start_block: BlockNumber,189}190191#[derive(Encode, Decode, Debug, Clone, PartialEq)]192#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]193pub struct CollectionLimits {194    pub account_token_ownership_limit: u32,195    pub sponsored_data_size: u32,196    pub token_limit: u32,197198    // Timeouts for item types in passed blocks199    pub sponsor_transfer_timeout: u32,200}201202impl Default for CollectionLimits {203    fn default() -> CollectionLimits {204        CollectionLimits { 205            account_token_ownership_limit: 10_000_000, 206            token_limit: u32::max_value(),207            sponsored_data_size: u32::max_value(), 208            sponsor_transfer_timeout: 14400 }209    }210}211212#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]213#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]214pub struct ChainLimits {215    pub collection_numbers_limit: u32,216    pub account_token_ownership_limit: u32,217    pub collections_admins_limit: u64,218    pub custom_data_limit: u32,219220    // Timeouts for item types in passed blocks221    pub nft_sponsor_transfer_timeout: u32,222    pub fungible_sponsor_transfer_timeout: u32,223    pub refungible_sponsor_transfer_timeout: u32,224}225226pub trait WeightInfo {227	fn create_collection() -> Weight;228	fn destroy_collection() -> Weight;229	fn add_to_white_list() -> Weight;230	fn remove_from_white_list() -> Weight;231    fn set_public_access_mode() -> Weight;232    fn set_mint_permission() -> Weight;233    fn change_collection_owner() -> Weight;234    fn add_collection_admin() -> Weight;235    fn remove_collection_admin() -> Weight;236    fn set_collection_sponsor() -> Weight;237    fn confirm_sponsorship() -> Weight;238    fn remove_collection_sponsor() -> Weight;239    fn create_item(s: usize) -> Weight;240    fn burn_item() -> Weight;241    fn transfer() -> Weight;242    fn approve() -> Weight;243    fn transfer_from() -> Weight;244    fn set_offchain_schema() -> Weight;245    fn set_const_on_chain_schema() -> Weight;246    fn set_variable_on_chain_schema() -> Weight;247    fn set_variable_meta_data() -> Weight;248    fn enable_contract_sponsoring() -> Weight;249}250251#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]252#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]253pub struct CreateNftData {254    pub const_data: Vec<u8>,255    pub variable_data: Vec<u8>,256}257258#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]259#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]260pub struct CreateFungibleData {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        CollectionLimitBoundsExceeded376	}377}378379pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {380    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;381382    /// Weight information for extrinsics in this pallet.383	type WeightInfo: WeightInfo;384}385386#[cfg(feature = "runtime-benchmarks")]387mod benchmarking;388389// #endregion390391decl_storage! {392    trait Store for Module<T: Trait> as Nft {393394        // Private members395        NextCollectionID: CollectionId;396        CreatedCollectionCount: u32;397        ChainVersion: u64;398        ItemListIndex: map hasher(identity) CollectionId => TokenId;399400        // Chain limits struct401        pub ChainLimit get(fn chain_limit) config(): ChainLimits;402403        // Bound counters404        CollectionCount: u32;405        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;406407        // Basic collections408        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;409        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;410        pub WhiteList get(fn white_list): map hasher(identity) CollectionId => Vec<T::AccountId>;411412        /// Balance owner per collection map413        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;414415        /// second parameter: item id + owner account id416        pub ApprovedList get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;417418        /// Item collections419        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;420        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => FungibleItemType<T::AccountId>;421        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;422423        /// Index list424        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;425426        /// Tokens transfer baskets427        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;428        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Vec<BasketItem<T::AccountId, T::BlockNumber>>;429        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;430431        // Contract Sponsorship and Ownership432        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;433        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;434        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;435        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;436    }437    add_extra_genesis {438        build(|config: &GenesisConfig<T>| {439            // Modification of storage440            for (_num, _c) in &config.collection {441                <Module<T>>::init_collection(_c);442            }443444            for (_num, _q, _i) in &config.nft_item_id {445                <Module<T>>::init_nft_token(_i);446            }447448            for (_num, _q, _i) in &config.fungible_item_id {449                <Module<T>>::init_fungible_token(_i);450            }451452            for (_num, _q, _i) in &config.refungible_item_id {453                <Module<T>>::init_refungible_token(_i);454            }455        })456    }457}458459decl_event!(460    pub enum Event<T>461    where462        AccountId = <T as system::Trait>::AccountId,463    {464        /// New collection was created465        /// 466        /// # Arguments467        /// 468        /// * collection_id: Globally unique identifier of newly created collection.469        /// 470        /// * mode: [CollectionMode] converted into u8.471        /// 472        /// * account_id: Collection owner.473        Created(CollectionId, u8, AccountId),474475        /// New item was created.476        /// 477        /// # Arguments478        /// 479        /// * collection_id: Id of the collection where item was created.480        /// 481        /// * item_id: Id of an item. Unique within the collection.482        ItemCreated(CollectionId, TokenId),483484        /// Collection item was burned.485        /// 486        /// # Arguments487        /// 488        /// collection_id.489        /// 490        /// item_id: Identifier of burned NFT.491        ItemDestroyed(CollectionId, TokenId),492    }493);494495decl_module! {496    pub struct Module<T: Trait> for enum Call where origin: T::Origin {497498        fn deposit_event() = default;499        type Error = Error<T>;500501        fn on_initialize(now: T::BlockNumber) -> Weight {502503            if ChainVersion::get() < 2504            {505                let value = NextCollectionID::get();506                CreatedCollectionCount::put(value);507                ChainVersion::put(2);508            }509510            0511        }512513        /// 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.514        /// 515        /// # Permissions516        /// 517        /// * Anyone.518        /// 519        /// # Arguments520        /// 521        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.522        /// 523        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.524        /// 525        /// * token_prefix: UTF-8 string with token prefix.526        /// 527        /// * mode: [CollectionMode] collection type and type dependent data.528        // returns collection ID529        #[weight = T::WeightInfo::create_collection()]530        pub fn create_collection(origin,531                                 collection_name: Vec<u16>,532                                 collection_description: Vec<u16>,533                                 token_prefix: Vec<u8>,534                                 mode: CollectionMode) -> DispatchResult {535536            // Anyone can create a collection537            let who = ensure_signed(origin)?;538539            let decimal_points = match mode {540                CollectionMode::Fungible(points) => points,541                CollectionMode::ReFungible(points) => points,542                _ => 0543            };544545            // bound Total number of collections546            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);547548            // check params549            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);550551            let mut name = collection_name.to_vec();552            name.push(0);553            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);554555            let mut description = collection_description.to_vec();556            description.push(0);557            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);558559            let mut prefix = token_prefix.to_vec();560            prefix.push(0);561            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);562563            // Generate next collection ID564            let next_id = CreatedCollectionCount::get()565                .checked_add(1)566                .ok_or(Error::<T>::NumOverflow)?;567568            // bound counter569            let total = CollectionCount::get()570                .checked_add(1)571                .ok_or(Error::<T>::NumOverflow)?;572573            CreatedCollectionCount::put(next_id);574            CollectionCount::put(total);575576            // Create new collection577            let new_collection = CollectionType {578                owner: who.clone(),579                name: name,580                mode: mode.clone(),581                mint_mode: false,582                access: AccessMode::Normal,583                description: description,584                decimal_points: decimal_points,585                token_prefix: prefix,586                offchain_schema: Vec::new(),587                schema_version: SchemaVersion::ImageURL,588                sponsor: T::AccountId::default(),589                unconfirmed_sponsor: T::AccountId::default(),590                variable_on_chain_schema: Vec::new(),591                const_on_chain_schema: Vec::new(),592                limits: CollectionLimits::default(),593            };594595            // Add new collection to map596            <Collection<T>>::insert(next_id, new_collection);597598            // call event599            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));600601            Ok(())602        }603604        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.605        /// 606        /// # Permissions607        /// 608        /// * Collection Owner.609        /// 610        /// # Arguments611        /// 612        /// * collection_id: collection to destroy.613        #[weight = T::WeightInfo::destroy_collection()]614        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {615616            let sender = ensure_signed(origin)?;617            Self::check_owner_permissions(collection_id, sender)?;618619            <AddressTokens<T>>::remove_prefix(collection_id);620            <ApprovedList<T>>::remove_prefix(collection_id);621            <Balance<T>>::remove_prefix(collection_id);622            <ItemListIndex>::remove(collection_id);623            <AdminList<T>>::remove(collection_id);624            <Collection<T>>::remove(collection_id);625            <WhiteList<T>>::remove(collection_id);626627            <NftItemList<T>>::remove_prefix(collection_id);628            <FungibleItemList<T>>::remove_prefix(collection_id);629            <ReFungibleItemList<T>>::remove_prefix(collection_id);630631            <NftTransferBasket<T>>::remove_prefix(collection_id);632            <FungibleTransferBasket<T>>::remove_prefix(collection_id);633            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);634635            if CollectionCount::get() > 0636            {637                // bound couter638                let total = CollectionCount::get()639                    .checked_sub(1)640                    .ok_or(Error::<T>::NumOverflow)?;641642                CollectionCount::put(total);643            }644645            Ok(())646        }647648        /// Add an address to white list.649        /// 650        /// # Permissions651        /// 652        /// * Collection Owner653        /// * Collection Admin654        /// 655        /// # Arguments656        /// 657        /// * collection_id.658        /// 659        /// * address.660        #[weight = T::WeightInfo::add_to_white_list()]661        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{662663            let sender = ensure_signed(origin)?;664            Self::check_owner_or_admin_permissions(collection_id, sender)?;665666            let mut white_list_collection: Vec<T::AccountId>;667            if <WhiteList<T>>::contains_key(collection_id) {668                white_list_collection = <WhiteList<T>>::get(collection_id);669                if !white_list_collection.contains(&address.clone())670                {671                    white_list_collection.push(address.clone());672                }673            }674            else {675                white_list_collection = Vec::new();676                white_list_collection.push(address.clone());677            }678679            <WhiteList<T>>::insert(collection_id, white_list_collection);680            Ok(())681        }682683        /// Remove an address from white list.684        /// 685        /// # Permissions686        /// 687        /// * Collection Owner688        /// * Collection Admin689        /// 690        /// # Arguments691        /// 692        /// * collection_id.693        /// 694        /// * address.695        #[weight = T::WeightInfo::remove_from_white_list()]696        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{697698            let sender = ensure_signed(origin)?;699            Self::check_owner_or_admin_permissions(collection_id, sender)?;700701            if <WhiteList<T>>::contains_key(collection_id) {702                let mut white_list_collection = <WhiteList<T>>::get(collection_id);703                if white_list_collection.contains(&address.clone())704                {705                    white_list_collection.retain(|i| *i != address.clone());706                    <WhiteList<T>>::insert(collection_id, white_list_collection);707                }708            }709710            Ok(())711        }712713        /// Toggle between normal and white list access for the methods with access for `Anyone`.714        /// 715        /// # Permissions716        /// 717        /// * Collection Owner.718        /// 719        /// # Arguments720        /// 721        /// * collection_id.722        /// 723        /// * mode: [AccessMode]724        #[weight = T::WeightInfo::set_public_access_mode()]725        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult726        {727            let sender = ensure_signed(origin)?;728729            Self::check_owner_permissions(collection_id, sender)?;730            let mut target_collection = <Collection<T>>::get(collection_id);731            target_collection.access = mode;732            <Collection<T>>::insert(collection_id, target_collection);733734            Ok(())735        }736737        /// Allows Anyone to create tokens if:738        /// * White List is enabled, and739        /// * Address is added to white list, and740        /// * This method was called with True parameter741        /// 742        /// # Permissions743        /// * Collection Owner744        ///745        /// # Arguments746        /// 747        /// * collection_id.748        /// 749        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.750        #[weight = T::WeightInfo::set_mint_permission()]751        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult752        {753            let sender = ensure_signed(origin)?;754755            Self::check_owner_permissions(collection_id, sender)?;756            let mut target_collection = <Collection<T>>::get(collection_id);757            target_collection.mint_mode = mint_permission;758            <Collection<T>>::insert(collection_id, target_collection);759760            Ok(())761        }762763        /// Change the owner of the collection.764        /// 765        /// # Permissions766        /// 767        /// * Collection Owner.768        /// 769        /// # Arguments770        /// 771        /// * collection_id.772        /// 773        /// * new_owner.774        #[weight = T::WeightInfo::change_collection_owner()]775        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {776777            let sender = ensure_signed(origin)?;778            Self::check_owner_permissions(collection_id, sender)?;779            let mut target_collection = <Collection<T>>::get(collection_id);780            target_collection.owner = new_owner;781            <Collection<T>>::insert(collection_id, target_collection);782783            Ok(())784        }785786        /// Adds an admin of the Collection.787        /// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership. 788        /// 789        /// # Permissions790        /// 791        /// * Collection Owner.792        /// * Collection Admin.793        /// 794        /// # Arguments795        /// 796        /// * collection_id: ID of the Collection to add admin for.797        /// 798        /// * new_admin_id: Address of new admin to add.799        #[weight = T::WeightInfo::add_collection_admin()]800        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {801802            let sender = ensure_signed(origin)?;803            Self::check_owner_or_admin_permissions(collection_id, sender)?;804            let mut admin_arr: Vec<T::AccountId> = Vec::new();805806            if <AdminList<T>>::contains_key(collection_id)807            {808                admin_arr = <AdminList<T>>::get(collection_id);809                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);810            }811812            // Number of collection admins813            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);814815            admin_arr.push(new_admin_id);816            <AdminList<T>>::insert(collection_id, admin_arr);817818            Ok(())819        }820821        /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.822        ///823        /// # Permissions824        /// 825        /// * Collection Owner.826        /// * Collection Admin.827        /// 828        /// # Arguments829        /// 830        /// * collection_id: ID of the Collection to remove admin for.831        /// 832        /// * account_id: Address of admin to remove.833        #[weight = T::WeightInfo::remove_collection_admin()]834        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {835836            let sender = ensure_signed(origin)?;837            Self::check_owner_or_admin_permissions(collection_id, sender)?;838839            if <AdminList<T>>::contains_key(collection_id)840            {841                let mut admin_arr = <AdminList<T>>::get(collection_id);842                admin_arr.retain(|i| *i != account_id);843                <AdminList<T>>::insert(collection_id, admin_arr);844            }845846            Ok(())847        }848849        /// # Permissions850        /// 851        /// * Collection Owner852        /// 853        /// # Arguments854        /// 855        /// * collection_id.856        /// 857        /// * new_sponsor.858        #[weight = T::WeightInfo::set_collection_sponsor()]859        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> 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.owner, Error::<T>::NoPermission);866867            target_collection.unconfirmed_sponsor = new_sponsor;868            <Collection<T>>::insert(collection_id, target_collection);869870            Ok(())871        }872873        /// # Permissions874        /// 875        /// * Sponsor.876        /// 877        /// # Arguments878        /// 879        /// * collection_id.880        #[weight = T::WeightInfo::confirm_sponsorship()]881        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {882883            let sender = ensure_signed(origin)?;884            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);885886            let mut target_collection = <Collection<T>>::get(collection_id);887            ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);888889            target_collection.sponsor = target_collection.unconfirmed_sponsor;890            target_collection.unconfirmed_sponsor = T::AccountId::default();891            <Collection<T>>::insert(collection_id, target_collection);892893            Ok(())894        }895896        /// Switch back to pay-per-own-transaction model.897        ///898        /// # Permissions899        ///900        /// * Collection owner.901        /// 902        /// # Arguments903        /// 904        /// * collection_id.905        #[weight = T::WeightInfo::remove_collection_sponsor()]906        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {907908            let sender = ensure_signed(origin)?;909            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);910911            let mut target_collection = <Collection<T>>::get(collection_id);912            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);913914            target_collection.sponsor = T::AccountId::default();915            <Collection<T>>::insert(collection_id, target_collection);916917            Ok(())918        }919920        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.921        /// 922        /// # Permissions923        /// 924        /// * Collection Owner.925        /// * Collection Admin.926        /// * Anyone if927        ///     * White List is enabled, and928        ///     * Address is added to white list, and929        ///     * MintPermission is enabled (see SetMintPermission method)930        /// 931        /// # Arguments932        /// 933        /// * collection_id: ID of the collection.934        /// 935        /// * owner: Address, initial owner of the NFT.936        ///937        /// * data: Token data to store on chain.938        // #[weight =939        // (130_000_000 as Weight)940        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))941        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))942        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]943944        #[weight = T::WeightInfo::create_item(data.len())]945        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {946947            let sender = ensure_signed(origin)?;948949            Self::collection_exists(collection_id)?;950951            let target_collection = <Collection<T>>::get(collection_id);952953            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;954            Self::validate_create_item_args(&target_collection, &data)?;955            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;956957            Ok(())958        }959960        /// This method creates multiple instances of NFT Collection created with CreateCollection method.961        /// 962        /// # Permissions963        /// 964        /// * Collection Owner.965        /// * Collection Admin.966        /// * Anyone if967        ///     * White List is enabled, and968        ///     * Address is added to white list, and969        ///     * MintPermission is enabled (see SetMintPermission method)970        /// 971        /// # Arguments972        /// 973        /// * collection_id: ID of the collection.974        /// 975        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].976        /// 977        /// * owner: Address, initial owner of the NFT.978        #[weight = T::WeightInfo::create_item(items_data.into_iter()979                               .map(|data| { data.len() })980                               .sum())]981        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {982983            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);984            let sender = ensure_signed(origin)?;985986            Self::collection_exists(collection_id)?;987            let target_collection = <Collection<T>>::get(collection_id);988989            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;990991            for data in &items_data {992                Self::validate_create_item_args(&target_collection, data)?;993            }994            for data in &items_data {995                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;996            }997998            Ok(())999        }10001001        /// Destroys a concrete instance of NFT.1002        /// 1003        /// # Permissions1004        /// 1005        /// * Collection Owner.1006        /// * Collection Admin.1007        /// * Current NFT Owner.1008        /// 1009        /// # Arguments1010        /// 1011        /// * collection_id: ID of the collection.1012        /// 1013        /// * item_id: ID of NFT to burn.1014        #[weight = T::WeightInfo::burn_item()]1015        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {10161017            let sender = ensure_signed(origin)?;1018            Self::collection_exists(collection_id)?;10191020            // Transfer permissions check1021            let target_collection = <Collection<T>>::get(collection_id);1022            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1023                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1024                Error::<T>::NoPermission);10251026            if target_collection.access == AccessMode::WhiteList {1027                Self::check_white_list(collection_id, &sender)?;1028            }10291030            match target_collection.mode1031            {1032                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1033                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,1034                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1035                _ => ()1036            };10371038            // call event1039            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10401041            Ok(())1042        }10431044        /// Change ownership of the token.1045        /// 1046        /// # Permissions1047        /// 1048        /// * Collection Owner1049        /// * Collection Admin1050        /// * Current NFT owner1051        ///1052        /// # Arguments1053        /// 1054        /// * recipient: Address of token recipient.1055        /// 1056        /// * collection_id.1057        /// 1058        /// * item_id: ID of the item1059        ///     * Non-Fungible Mode: Required.1060        ///     * Fungible Mode: Ignored.1061        ///     * Re-Fungible Mode: Required.1062        /// 1063        /// * value: Amount to transfer.1064        ///     * Non-Fungible Mode: Ignored1065        ///     * Fungible Mode: Must specify transferred amount1066        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1067        #[weight = T::WeightInfo::transfer()]1068        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10691070            let sender = ensure_signed(origin)?;1071            let target_collection = <Collection<T>>::get(collection_id);10721073            // Limits check1074            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;10751076            // Transfer permissions check1077            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1078                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1079                Error::<T>::NoPermission);10801081            if target_collection.access == AccessMode::WhiteList {1082                Self::check_white_list(collection_id, &sender)?;1083                Self::check_white_list(collection_id, &recipient)?;1084            }10851086            match target_collection.mode1087            {1088                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1089                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1090                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1091                _ => ()1092            };10931094            Ok(())1095        }10961097        /// Set, change, or remove approved address to transfer the ownership of the NFT.1098        /// 1099        /// # Permissions1100        /// 1101        /// * Collection Owner1102        /// * Collection Admin1103        /// * Current NFT owner1104        /// 1105        /// # Arguments1106        /// 1107        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1108        /// 1109        /// * collection_id.1110        /// 1111        /// * item_id: ID of the item.1112        #[weight = T::WeightInfo::approve()]1113        pub fn approve(origin, approved: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {11141115            let sender = ensure_signed(origin)?;11161117            // Transfer permissions check1118            let target_collection = <Collection<T>>::get(collection_id);1119            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1120                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1121                Error::<T>::NoPermission);11221123            if target_collection.access == AccessMode::WhiteList {1124                Self::check_white_list(collection_id, &sender)?;1125                Self::check_white_list(collection_id, &approved)?;1126            }11271128            // amount param stub1129            let amount = 100000000;11301131            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1132            if list_exists {11331134                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1135                let item_contains = list.iter().any(|i| i.approved == approved);11361137                if !item_contains {1138                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1139                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1140                }1141            } else {11421143                let mut list = Vec::new();1144                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1145                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1146            }11471148            Ok(())1149        }1150        1151        /// 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.1152        /// 1153        /// # Permissions1154        /// * Collection Owner1155        /// * Collection Admin1156        /// * Current NFT owner1157        /// * Address approved by current NFT owner1158        /// 1159        /// # Arguments1160        /// 1161        /// * from: Address that owns token.1162        /// 1163        /// * recipient: Address of token recipient.1164        /// 1165        /// * collection_id.1166        /// 1167        /// * item_id: ID of the item.1168        /// 1169        /// * value: Amount to transfer.1170        #[weight = T::WeightInfo::transfer_from()]1171        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11721173            let sender = ensure_signed(origin)?;1174            let mut appoved_transfer = false;11751176            // Check approve1177            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1178                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1179                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1180                if opt_item.is_some()1181                {1182                    appoved_transfer = true;1183                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1184                }1185            }11861187            let target_collection = <Collection<T>>::get(collection_id);11881189            // Limits check1190            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11911192            // Transfer permissions check         1193            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1194            Error::<T>::NoPermission);11951196            if target_collection.access == AccessMode::WhiteList {1197                Self::check_white_list(collection_id, &sender)?;1198                Self::check_white_list(collection_id, &recipient)?;1199            }12001201            // remove approve1202            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1203                .into_iter().filter(|i| i.approved != sender.clone()).collect();1204            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);120512061207            match target_collection.mode1208            {1209                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1210                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1211                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1212                _ => ()1213            };12141215            Ok(())1216        }12171218        #[weight = 0]1219        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {12201221            // let no_perm_mes = "You do not have permissions to modify this collection";1222            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1223            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1224            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);12251226            // // on_nft_received  call12271228            // Self::transfer(origin, collection_id, item_id, new_owner)?;12291230            Ok(())1231        }12321233        /// Set off-chain data schema.1234        /// 1235        /// # Permissions1236        /// 1237        /// * Collection Owner1238        /// * Collection Admin1239        /// 1240        /// # Arguments1241        /// 1242        /// * collection_id.1243        /// 1244        /// * schema: String representing the offchain data schema.1245        #[weight = T::WeightInfo::set_variable_meta_data()]1246        pub fn set_variable_meta_data (1247            origin,1248            collection_id: CollectionId,1249            item_id: TokenId,1250            data: Vec<u8>1251        ) -> DispatchResult {1252            let sender = ensure_signed(origin)?;1253            1254            Self::collection_exists(collection_id)?;1255            1256            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12571258            // Modify permissions check1259            let target_collection = <Collection<T>>::get(collection_id);1260            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1261                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1262                Error::<T>::NoPermission);12631264            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12651266            match target_collection.mode1267            {1268                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1269                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1270                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1271                _ => fail!(Error::<T>::UnexpectedCollectionType)1272            };12731274            Ok(())1275        }1276 1277        /// Set schema standard1278        /// ImageURL1279        /// Unique1280        /// 1281        /// # Permissions1282        /// 1283        /// * Collection Owner1284        /// * Collection Admin1285        /// 1286        /// # Arguments1287        /// 1288        /// * collection_id.1289        /// 1290        /// * schema: SchemaVersion: enum1291        #[weight = 0]1292        pub fn set_schema_version(1293            origin,1294            collection_id: CollectionId,1295            version: SchemaVersion1296        ) -> DispatchResult {1297            let sender = ensure_signed(origin)?;1298            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1299            let mut target_collection = <Collection<T>>::get(collection_id);1300            target_collection.schema_version = version;1301            <Collection<T>>::insert(collection_id, target_collection);13021303            Ok(())1304        }13051306        /// Set off-chain data schema.1307        /// 1308        /// # Permissions1309        /// 1310        /// * Collection Owner1311        /// * Collection Admin1312        /// 1313        /// # Arguments1314        /// 1315        /// * collection_id.1316        /// 1317        /// * schema: String representing the offchain data schema.1318        #[weight = T::WeightInfo::set_offchain_schema()]1319        pub fn set_offchain_schema(1320            origin,1321            collection_id: CollectionId,1322            schema: Vec<u8>1323        ) -> DispatchResult {1324            let sender = ensure_signed(origin)?;1325            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13261327            let mut target_collection = <Collection<T>>::get(collection_id);1328            target_collection.offchain_schema = schema;1329            <Collection<T>>::insert(collection_id, target_collection);13301331            Ok(())1332        }13331334        /// Set const on-chain data schema.1335        /// 1336        /// # Permissions1337        /// 1338        /// * Collection Owner1339        /// * Collection Admin1340        /// 1341        /// # Arguments1342        /// 1343        /// * collection_id.1344        /// 1345        /// * schema: String representing the const on-chain data schema.1346        #[weight = T::WeightInfo::set_const_on_chain_schema()]1347        pub fn set_const_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            let mut target_collection = <Collection<T>>::get(collection_id);1356            target_collection.const_on_chain_schema = schema;1357            <Collection<T>>::insert(collection_id, target_collection);13581359            Ok(())1360        }13611362        /// Set variable on-chain data schema.1363        /// 1364        /// # Permissions1365        /// 1366        /// * Collection Owner1367        /// * Collection Admin1368        /// 1369        /// # Arguments1370        /// 1371        /// * collection_id.1372        /// 1373        /// * schema: String representing the variable on-chain data schema.1374        #[weight = T::WeightInfo::set_const_on_chain_schema()]1375        pub fn set_variable_on_chain_schema (1376            origin,1377            collection_id: CollectionId,1378            schema: Vec<u8>1379        ) -> DispatchResult {1380            let sender = ensure_signed(origin)?;1381            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13821383            let mut target_collection = <Collection<T>>::get(collection_id);1384            target_collection.variable_on_chain_schema = schema;1385            <Collection<T>>::insert(collection_id, target_collection);13861387            Ok(())1388        }13891390        // Sudo permissions function1391        #[weight = 0]1392        pub fn set_chain_limits(1393            origin,1394            limits: ChainLimits1395        ) -> DispatchResult {1396            ensure_root(origin)?;1397            <ChainLimit>::put(limits);1398            Ok(())1399        }14001401        /// Enable smart contract self-sponsoring.1402        /// 1403        /// # Permissions1404        /// 1405        /// * Contract Owner1406        /// 1407        /// # Arguments1408        /// 1409        /// * contract address1410        /// * enable flag1411        /// 1412        #[weight = T::WeightInfo::enable_contract_sponsoring()]1413        pub fn enable_contract_sponsoring(1414            origin,1415            contract_address: T::AccountId,1416            enable: bool1417        ) -> DispatchResult {14181419            let sender = ensure_signed(origin)?;14201421            #[cfg(feature = "runtime-benchmarks")]1422            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14231424            let mut is_owner = false;1425            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1426                let owner = <ContractOwner<T>>::get(&contract_address);1427                is_owner = sender == owner;1428            }1429            ensure!(is_owner, Error::<T>::NoPermission);14301431            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1432            Ok(())1433        }14341435        /// Set the rate limit for contract sponsoring to specified number of blocks.1436        /// 1437        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1438        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1439        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1440        /// from contract endowment if there are at least B blocks between such transactions. 1441        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1442        /// 1443        /// # Permissions1444        /// 1445        /// * Contract Owner1446        /// 1447        /// # Arguments1448        /// 1449        /// -`contract_address`: Address of the contract to sponsor1450        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1451        /// 1452        #[weight = 0]1453        pub fn set_contract_sponsoring_rate_limit(1454            origin,1455            contract_address: T::AccountId,1456            rate_limit: T::BlockNumber1457        ) -> DispatchResult {1458            let sender = ensure_signed(origin)?;1459            let mut is_owner = false;1460            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1461                let owner = <ContractOwner<T>>::get(&contract_address);1462                is_owner = sender == owner;1463            }1464            ensure!(is_owner, Error::<T>::NoPermission);14651466            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1467            Ok(())1468        }14691470        #[weight = 0]1471        pub fn set_collection_limits(1472            origin,1473            collection_id: u32,1474            limits: CollectionLimits,1475        ) -> DispatchResult {1476            let sender = ensure_signed(origin)?;1477            Self::check_owner_permissions(collection_id, sender.clone())?;1478            let mut target_collection = <Collection<T>>::get(collection_id);1479            let chain_limits = ChainLimit::get();1480            let climits = target_collection.limits;14811482            // collection bounds1483            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1484                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1485                Error::<T>::CollectionLimitBoundsExceeded);14861487            // token_limit   check  prev1488            ensure!(climits.token_limit > limits.token_limit && 1489                limits.token_limit <= chain_limits.account_token_ownership_limit, 1490                Error::<T>::AccountTokenLimitExceeded);14911492            target_collection.limits = limits;1493            <Collection<T>>::insert(collection_id, target_collection);14941495            Ok(())1496        } 1497    }1498}14991500impl<T: Trait> Module<T> {15011502    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15031504        // check token limit and account token limit1505        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1506        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1507        1508        Ok(())1509    }15101511    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15121513        // check token limit and account token limit1514        let total_items: u32 = ItemListIndex::get(collection_id);1515        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1516        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1517        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15181519        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1520            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1521            Self::check_white_list(collection_id, owner)?;1522            Self::check_white_list(collection_id, sender)?;1523        }15241525        Ok(())1526    }15271528    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1529        match target_collection.mode1530        {1531            CollectionMode::NFT => {1532                if let CreateItemData::NFT(data) = data {1533                    // check sizes1534                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1535                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1536                } else {1537                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1538                }1539            },1540            CollectionMode::Fungible(_) => {1541                if let CreateItemData::Fungible(_) = data {1542                } else {1543                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1544                }1545            },1546            CollectionMode::ReFungible(_) => {1547                if let CreateItemData::ReFungible(data) = data {15481549                    // check sizes1550                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1551                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1552                } else {1553                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1554                }1555            },1556            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1557        };15581559        Ok(())1560    }15611562    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1563        match data1564        {1565            CreateItemData::NFT(data) => {1566                let item = NftItemType {1567                    collection: collection_id,1568                    owner,1569                    const_data: data.const_data,1570                    variable_data: data.variable_data1571                };15721573                Self::add_nft_item(item)?;1574            },1575            CreateItemData::Fungible(_) => {1576                let item = FungibleItemType {1577                    collection: collection_id,1578                    owner,1579                    value: (10 as u128).pow(collection.decimal_points as u32)1580                };15811582                Self::add_fungible_item(item)?;1583            },1584            CreateItemData::ReFungible(data) => {1585                let mut owner_list = Vec::new();1586                let value = (10 as u128).pow(collection.decimal_points as u32);1587                owner_list.push(Ownership {owner: owner.clone(), fraction: value});15881589                let item = ReFungibleItemType {1590                    collection: collection_id,1591                    owner: owner_list,1592                    const_data: data.const_data,1593                    variable_data: data.variable_data1594                };15951596                Self::add_refungible_item(item)?;1597            }1598        };15991600        // call event1601        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16021603        Ok(())1604    }16051606    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1607        let current_index = <ItemListIndex>::get(item.collection)1608            .checked_add(1)1609            .ok_or(Error::<T>::NumOverflow)?;1610        let itemcopy = item.clone();1611        let owner = item.owner.clone();16121613        Self::add_token_index(item.collection, current_index, owner.clone())?;16141615        <ItemListIndex>::insert(item.collection, current_index);1616        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);16171618        // Add current block1619        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1620        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1621        1622        // Update balance1623        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1624            .checked_add(item.value)1625            .ok_or(Error::<T>::NumOverflow)?;1626        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);16271628        Ok(())1629    }16301631    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1632        let current_index = <ItemListIndex>::get(item.collection)1633            .checked_add(1)1634            .ok_or(Error::<T>::NumOverflow)?;1635        let itemcopy = item.clone();16361637        let value = item.owner.first().unwrap().fraction;1638        let owner = item.owner.first().unwrap().owner.clone();16391640        Self::add_token_index(item.collection, current_index, owner.clone())?;16411642        <ItemListIndex>::insert(item.collection, current_index);1643        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);16441645        // Add current block1646        let block_number: T::BlockNumber = 0.into();1647        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);16481649        // Update balance1650        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1651            .checked_add(value)1652            .ok_or(Error::<T>::NumOverflow)?;1653        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);16541655        Ok(())1656    }16571658    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1659        let current_index = <ItemListIndex>::get(item.collection)1660            .checked_add(1)1661            .ok_or(Error::<T>::NumOverflow)?;16621663        let item_owner = item.owner.clone();1664        let collection_id = item.collection.clone();1665        Self::add_token_index(collection_id, current_index, item.owner.clone())?;16661667        <ItemListIndex>::insert(collection_id, current_index);1668        <NftItemList<T>>::insert(collection_id, current_index, item);16691670        // Add current block1671        let block_number: T::BlockNumber = 0.into();1672        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);16731674        // Update balance1675        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1676            .checked_add(1)1677            .ok_or(Error::<T>::NumOverflow)?;1678        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);16791680        Ok(())1681    }16821683    fn burn_refungible_item(1684        collection_id: CollectionId,1685        item_id: TokenId,1686        owner: T::AccountId,1687    ) -> DispatchResult {1688        ensure!(1689            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1690            Error::<T>::TokenNotFound1691        );1692        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1693        let item = collection1694            .owner1695            .iter()1696            .filter(|&i| i.owner == owner)1697            .next()1698            .unwrap();1699        Self::remove_token_index(collection_id, item_id, owner.clone())?;17001701        // remove approve list1702        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));17031704        // update balance1705        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1706            .checked_sub(item.fraction)1707            .ok_or(Error::<T>::NumOverflow)?;1708        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17091710        <ReFungibleItemList<T>>::remove(collection_id, item_id);17111712        Ok(())1713    }17141715    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1716        ensure!(1717            <NftItemList<T>>::contains_key(collection_id, item_id),1718            Error::<T>::TokenNotFound1719        );1720        let item = <NftItemList<T>>::get(collection_id, item_id);1721        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17221723        // remove approve list1724        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));17251726        // update balance1727        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1728            .checked_sub(1)1729            .ok_or(Error::<T>::NumOverflow)?;1730        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1731        <NftItemList<T>>::remove(collection_id, item_id);17321733        Ok(())1734    }17351736    fn burn_fungible_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1737        ensure!(1738            <FungibleItemList<T>>::contains_key(collection_id, item_id),1739            Error::<T>::TokenNotFound1740        );1741        let item = <FungibleItemList<T>>::get(collection_id, item_id);1742        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17431744        // remove approve list1745        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));17461747        // update balance1748        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1749            .checked_sub(item.value)1750            .ok_or(Error::<T>::NumOverflow)?;1751        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17521753        <FungibleItemList<T>>::remove(collection_id, item_id);17541755        Ok(())1756    }17571758    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1759        ensure!(1760            <Collection<T>>::contains_key(collection_id),1761            Error::<T>::CollectionNotFound1762        );1763        Ok(())1764    }17651766    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1767        Self::collection_exists(collection_id)?;17681769        let target_collection = <Collection<T>>::get(collection_id);1770        ensure!(1771            subject == target_collection.owner,1772            Error::<T>::NoPermission1773        );17741775        Ok(())1776    }17771778    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1779        let target_collection = <Collection<T>>::get(collection_id);1780        let mut result: bool = subject == target_collection.owner;1781        let exists = <AdminList<T>>::contains_key(collection_id);17821783        if !result & exists {1784            if <AdminList<T>>::get(collection_id).contains(&subject) {1785                result = true1786            }1787        }17881789        result1790    }17911792    fn check_owner_or_admin_permissions(1793        collection_id: CollectionId,1794        subject: T::AccountId,1795    ) -> DispatchResult {1796        Self::collection_exists(collection_id)?;1797        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());17981799        ensure!(1800            result,1801            Error::<T>::NoPermission1802        );1803        Ok(())1804    }18051806    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1807        let target_collection = <Collection<T>>::get(collection_id);18081809        match target_collection.mode {1810            CollectionMode::NFT => {1811                <NftItemList<T>>::get(collection_id, item_id).owner == subject1812            }1813            CollectionMode::Fungible(_) => {1814                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1815            }1816            CollectionMode::ReFungible(_) => {1817                <ReFungibleItemList<T>>::get(collection_id, item_id)1818                    .owner1819                    .iter()1820                    .any(|i| i.owner == subject)1821            }1822            CollectionMode::Invalid => false,1823        }1824    }18251826    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1827        let mes = Error::<T>::AddresNotInWhiteList;1828        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1829        let wl = <WhiteList<T>>::get(collection_id);1830        ensure!(wl.contains(address), mes);18311832        Ok(())1833    }18341835    fn transfer_fungible(1836        collection_id: CollectionId,1837        item_id: TokenId,1838        value: u128,1839        owner: T::AccountId,1840        new_owner: T::AccountId,1841    ) -> DispatchResult {1842        ensure!(1843            <FungibleItemList<T>>::contains_key(collection_id, item_id),1844            Error::<T>::TokenNotFound1845        );18461847        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1848        let amount = full_item.value;18491850        ensure!(amount >= value, Error::<T>::TokenValueTooLow);18511852        // update balance1853        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1854            .checked_sub(value)1855            .ok_or(Error::<T>::NumOverflow)?;1856        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);18571858        let mut new_owner_account_id = 0;1859        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1860        if new_owner_items.len() > 0 {1861            new_owner_account_id = new_owner_items[0];1862        }18631864        // transfer1865        if amount == value && new_owner_account_id == 0 {1866            // change owner1867            // new owner do not have account1868            let mut new_full_item = full_item.clone();1869            new_full_item.owner = new_owner.clone();1870            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18711872            // update balance1873            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1874                .checked_add(value)1875                .ok_or(Error::<T>::NumOverflow)?;1876            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18771878            // update index collection1879            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1880        } else {1881            let mut new_full_item = full_item.clone();1882            new_full_item.value -= value;18831884            // separate amount1885            if new_owner_account_id > 0 {1886                // new owner has account1887                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1888                item.value += value;18891890                // update balance1891                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1892                    .checked_add(value)1893                    .ok_or(Error::<T>::NumOverflow)?;1894                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18951896                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1897            } else {1898                // new owner do not have account1899                let item = FungibleItemType {1900                    collection: collection_id,1901                    owner: new_owner.clone(),1902                    value1903                };19041905                Self::add_fungible_item(item)?;1906            }19071908            if amount == value {1909                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;19101911                // remove approve list1912                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1913                <FungibleItemList<T>>::remove(collection_id, item_id);1914            }19151916            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1917        }19181919        Ok(())1920    }19211922    fn transfer_refungible(1923        collection_id: CollectionId,1924        item_id: TokenId,1925        value: u128,1926        owner: T::AccountId,1927        new_owner: T::AccountId,1928    ) -> DispatchResult {1929        ensure!(1930            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1931            Error::<T>::TokenNotFound1932        );19331934        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1935        let item = full_item1936            .owner1937            .iter()1938            .filter(|i| i.owner == owner)1939            .next()1940            .ok_or(Error::<T>::NumOverflow)?;1941        let amount = item.fraction;19421943        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19441945        // update balance1946        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1947            .checked_sub(value)1948            .ok_or(Error::<T>::NumOverflow)?;1949        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19501951        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1952            .checked_add(value)1953            .ok_or(Error::<T>::NumOverflow)?;1954        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19551956        let old_owner = item.owner.clone();1957        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19581959        // transfer1960        if amount == value && !new_owner_has_account {1961            // change owner1962            // new owner do not have account1963            let mut new_full_item = full_item.clone();1964            new_full_item1965                .owner1966                .iter_mut()1967                .find(|i| i.owner == owner)1968                .unwrap()1969                .owner = new_owner.clone();1970            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19711972            // update index collection1973            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1974        } else {1975            let mut new_full_item = full_item.clone();1976            new_full_item1977                .owner1978                .iter_mut()1979                .find(|i| i.owner == owner)1980                .unwrap()1981                .fraction -= value;19821983            // separate amount1984            if new_owner_has_account {1985                // new owner has account1986                new_full_item1987                    .owner1988                    .iter_mut()1989                    .find(|i| i.owner == new_owner)1990                    .unwrap()1991                    .fraction += value;1992            } else {1993                // new owner do not have account1994                new_full_item.owner.push(Ownership {1995                    owner: new_owner.clone(),1996                    fraction: value,1997                });1998                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1999            }20002001            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2002        }20032004        Ok(())2005    }20062007    fn transfer_nft(2008        collection_id: CollectionId,2009        item_id: TokenId,2010        sender: T::AccountId,2011        new_owner: T::AccountId,2012    ) -> DispatchResult {2013        ensure!(2014            <NftItemList<T>>::contains_key(collection_id, item_id),2015            Error::<T>::TokenNotFound2016        );20172018        let mut item = <NftItemList<T>>::get(collection_id, item_id);20192020        ensure!(2021            sender == item.owner,2022            Error::<T>::MustBeTokenOwner2023        );20242025        // update balance2026        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2027            .checked_sub(1)2028            .ok_or(Error::<T>::NumOverflow)?;2029        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20302031        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())2032            .checked_add(1)2033            .ok_or(Error::<T>::NumOverflow)?;2034        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);20352036        // change owner2037        let old_owner = item.owner.clone();2038        item.owner = new_owner.clone();2039        <NftItemList<T>>::insert(collection_id, item_id, item);20402041        // update index collection2042        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;20432044        // reset approved list2045        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));2046        Ok(())2047    }2048    2049    fn item_exists(2050        collection_id: CollectionId,2051        item_id: TokenId,2052        mode: &CollectionMode2053    ) -> DispatchResult {2054        match mode {2055            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2056            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2057            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2058            _ => ()2059        };2060        2061        Ok(())2062    }20632064    fn set_re_fungible_variable_data(2065        collection_id: CollectionId,2066        item_id: TokenId,2067        data: Vec<u8>2068    ) -> DispatchResult {2069        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20702071        item.variable_data = data;20722073        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20742075        Ok(())2076    }20772078    fn set_nft_variable_data(2079        collection_id: CollectionId,2080        item_id: TokenId,2081        data: Vec<u8>2082    ) -> DispatchResult {2083        let mut item = <NftItemList<T>>::get(collection_id, item_id);2084        2085        item.variable_data = data;20862087        <NftItemList<T>>::insert(collection_id, item_id, item);2088        2089        Ok(())2090    }20912092    fn init_collection(item: &CollectionType<T::AccountId>) {2093        // check params2094        assert!(2095            item.decimal_points <= MAX_DECIMAL_POINTS,2096            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2097        );2098        assert!(2099            item.name.len() <= 64,2100            "Collection name can not be longer than 63 char"2101        );2102        assert!(2103            item.name.len() <= 256,2104            "Collection description can not be longer than 255 char"2105        );2106        assert!(2107            item.token_prefix.len() <= 16,2108            "Token prefix can not be longer than 15 char"2109        );21102111        // Generate next collection ID2112        let next_id = CreatedCollectionCount::get()2113            .checked_add(1)2114            .unwrap();21152116        CreatedCollectionCount::put(next_id);2117    }21182119    fn init_nft_token(item: &NftItemType<T::AccountId>) {2120        let current_index = <ItemListIndex>::get(item.collection)2121            .checked_add(1)2122            .unwrap();21232124        let item_owner = item.owner.clone();2125        let collection_id = item.collection.clone();2126        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();21272128        <ItemListIndex>::insert(collection_id, current_index);21292130        // Update balance2131        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2132            .checked_add(1)2133            .unwrap();2134        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2135    }21362137    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2138        let current_index = <ItemListIndex>::get(item.collection)2139            .checked_add(1)2140            .unwrap();2141        let owner = item.owner.clone();21422143        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();21442145        <ItemListIndex>::insert(item.collection, current_index);21462147        // Update balance2148        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2149            .checked_add(item.value)2150            .unwrap();2151        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2152    }21532154    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2155        let current_index = <ItemListIndex>::get(item.collection)2156            .checked_add(1)2157            .unwrap();21582159        let value = item.owner.first().unwrap().fraction;2160        let owner = item.owner.first().unwrap().owner.clone();21612162        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();21632164        <ItemListIndex>::insert(item.collection, current_index);21652166        // Update balance2167        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2168            .checked_add(value)2169            .unwrap();2170        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2171    }21722173    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {21742175        // add to account limit2176        if <AccountItemCount<T>>::contains_key(owner.clone()) {21772178            // bound Owned tokens by a single address2179            let count = <AccountItemCount<T>>::get(owner.clone());2180            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21812182            <AccountItemCount<T>>::insert(owner.clone(), count2183                .checked_add(1)2184                .ok_or(Error::<T>::NumOverflow)?);2185        }2186        else {2187            <AccountItemCount<T>>::insert(owner.clone(), 1);2188        }21892190        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2191        if list_exists {2192            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2193            let item_contains = list.contains(&item_index.clone());21942195            if !item_contains {2196                list.push(item_index.clone());2197            }21982199            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2200        } else {2201            let mut itm = Vec::new();2202            itm.push(item_index.clone());2203            <AddressTokens<T>>::insert(collection_id, owner, itm);2204            2205        }22062207        Ok(())2208    }22092210    fn remove_token_index(2211        collection_id: CollectionId,2212        item_index: TokenId,2213        owner: T::AccountId,2214    ) -> DispatchResult {22152216        // update counter2217        <AccountItemCount<T>>::insert(owner.clone(), 2218            <AccountItemCount<T>>::get(owner.clone())2219            .checked_sub(1)2220            .ok_or(Error::<T>::NumOverflow)?);222122222223        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2224        if list_exists {2225            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2226            let item_contains = list.contains(&item_index.clone());22272228            if item_contains {2229                list.retain(|&item| item != item_index);2230                <AddressTokens<T>>::insert(collection_id, owner, list);2231            }2232        }22332234        Ok(())2235    }22362237    fn move_token_index(2238        collection_id: CollectionId,2239        item_index: TokenId,2240        old_owner: T::AccountId,2241        new_owner: T::AccountId,2242    ) -> DispatchResult {2243        Self::remove_token_index(collection_id, item_index, old_owner)?;2244        Self::add_token_index(collection_id, item_index, new_owner)?;22452246        Ok(())2247    }2248}22492250////////////////////////////////////////////////////////////////////////////////////////////////////2251// Economic models2252// #region22532254/// Fee multiplier.2255pub type Multiplier = FixedU128;22562257type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2258    <T as system::Trait>::AccountId,2259>>::Balance;2260type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2261    <T as system::Trait>::AccountId,2262>>::NegativeImbalance;22632264/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2265/// in the queue.2266#[derive(Encode, Decode, Clone, Eq, PartialEq)]2267pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2268    #[codec(compact)] BalanceOf<T>2269);22702271impl<T: Trait + Send + Sync> sp_std::fmt::Debug2272    for ChargeTransactionPayment<T>2273{2274    #[cfg(feature = "std")]2275    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2276        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2277    }2278    #[cfg(not(feature = "std"))]2279    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2280        Ok(())2281    }2282}22832284impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2285where2286    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2287    BalanceOf<T>: Send + Sync + FixedPointOperand,2288{2289    /// utility constructor. Used only in client/factory code.2290    pub fn from(fee: BalanceOf<T>) -> Self {2291        Self(fee)2292    }22932294    pub fn traditional_fee(2295        len: usize,2296        info: &DispatchInfoOf<T::Call>,2297        tip: BalanceOf<T>,2298    ) -> BalanceOf<T>2299    where2300        T::Call: Dispatchable<Info = DispatchInfo>,2301    {2302        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2303    }23042305	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2306		let weight_saturation = T::MaximumBlockWeight::get() / info.weight.max(1);2307		let len_saturation = T::MaximumBlockLength::get() as u64 / (len as u64).max(1);2308		let coefficient: BalanceOf<T> = weight_saturation.min(len_saturation).saturated_into::<BalanceOf<T>>();2309		final_fee.saturating_mul(coefficient).saturated_into::<TransactionPriority>()2310	}23112312    fn withdraw_fee(2313        &self,2314        who: &T::AccountId,2315        call: &T::Call,2316        info: &DispatchInfoOf<T::Call>,2317        len: usize,2318    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2319        let tip = self.0;23202321        // Set fee based on call type. Creating collection costs 1 Unique.2322        // All other transactions have traditional fees so far2323        // let fee = match call.is_sub_type() {2324        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2325        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2326        //                                                 // _ => <BalanceOf<T>>::from(100)2327        // };2328        let fee = Self::traditional_fee(len, info, tip);23292330        // Determine who is paying transaction fee based on ecnomic model2331        // Parse call to extract collection ID and access collection sponsor2332        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2333            Some(Call::create_item(collection_id, _owner, _properties)) => {23342335                // check free create limit2336                if <Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)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 _collection_limits = <Collection<T>>::get(collection_id).limits;2346                let _collection_mode = <Collection<T>>::get(collection_id).mode;23472348                // sponsor timeout2349                let sponsor_transfer = match _collection_mode {2350                    CollectionMode::NFT => {23512352                        // get correct limit2353                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2354                            _collection_limits.sponsor_transfer_timeout2355                        } else {2356                            ChainLimit::get().nft_sponsor_transfer_timeout2357                        };23582359                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2360                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2361                        let limit_time = basket + limit.into();2362                        if block_number >= limit_time {2363                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2364                            true2365                        }2366                        else {2367                            false2368                        }2369                    }2370                    CollectionMode::Fungible(_) => {23712372                        // get correct limit2373                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2374                            _collection_limits.sponsor_transfer_timeout2375                        } else {2376                            ChainLimit::get().fungible_sponsor_transfer_timeout2377                        };23782379                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2380                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2381                        if basket.iter().any(|i| i.address == _new_owner.clone())2382                        {2383                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2384                            let limit_time = item.start_block + limit.into();2385                            if block_number >= limit_time {2386                                basket.retain(|x| x.address == item.address);2387                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2388                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2389                                true2390                            }2391                            else {2392                                false2393                            }2394                        }2395                        else {2396                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2397                            true2398                        }2399                    }2400                    CollectionMode::ReFungible(_) => {24012402                        // get correct limit2403                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2404                            _collection_limits.sponsor_transfer_timeout2405                        } else {2406                            ChainLimit::get().refungible_sponsor_transfer_timeout2407                        };24082409                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2410                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2411                        let limit_time = basket + limit.into();2412                        if block_number >= limit_time {2413                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2414                            true2415                        } else {2416                            false2417                        }2418                    }2419                    _ => {2420                        false2421                    },2422                };24232424                if !sponsor_transfer {2425                    T::AccountId::default()2426                } else {2427                    <Collection<T>>::get(collection_id).sponsor2428                }2429            }24302431            _ => T::AccountId::default(),2432        };24332434        // Sponsor smart contracts2435        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {24362437            // On instantiation: set the contract owner2438            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {24392440                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2441                    code_hash,2442                    &data,2443                    &who,2444                );2445                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());24462447                T::AccountId::default()2448            },24492450            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2451            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {24522453                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());24542455                let mut sponsor_transfer = false;2456                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2457                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2458                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2459                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2460                    let limit_time = last_tx_block + rate_limit;24612462                    if block_number >= limit_time {2463                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2464                        sponsor_transfer = true;2465                    }2466                } else {2467                    sponsor_transfer = false;2468                }2469               2470                2471                let mut sp = T::AccountId::default();2472                if sponsor_transfer {2473                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2474                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2475                            sp = called_contract;2476                        }2477                    }2478                }24792480                sp2481            },24822483            _ => sponsor,2484        };24852486        let mut who_pays_fee: T::AccountId = sponsor.clone();2487        if sponsor == T::AccountId::default() {2488            who_pays_fee = who.clone();2489        }24902491        // Only mess with balances if fee is not zero.2492        if fee.is_zero() {2493            return Ok((fee, None));2494        }24952496        match <T as transaction_payment::Trait>::Currency::withdraw(2497            &who_pays_fee,2498            fee,2499            if tip.is_zero() {2500                WithdrawReason::TransactionPayment.into()2501            } else {2502                WithdrawReason::TransactionPayment | WithdrawReason::Tip2503            },2504            ExistenceRequirement::KeepAlive,2505        ) {2506            Ok(imbalance) => Ok((fee, Some(imbalance))),2507            Err(_) => Err(InvalidTransaction::Payment.into()),2508        }2509    }2510}251125122513impl<T: Trait + Send + Sync> SignedExtension2514    for ChargeTransactionPayment<T>2515where2516    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2517    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2518{2519    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2520    type AccountId = T::AccountId;2521    type Call = T::Call;2522    type AdditionalSigned = ();2523    type Pre = (2524        BalanceOf<T>,2525        Self::AccountId,2526        Option<NegativeImbalanceOf<T>>,2527        BalanceOf<T>,2528    );2529    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2530        Ok(())2531    }25322533    fn validate(2534        &self,2535        who: &Self::AccountId,2536        call: &Self::Call,2537        info: &DispatchInfoOf<Self::Call>,2538        len: usize,2539    ) -> TransactionValidity {2540		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2541		Ok(ValidTransaction {2542			priority: Self::get_priority(len, info, fee),2543			..Default::default()2544		})2545    }25462547    fn pre_dispatch(2548        self,2549        who: &Self::AccountId,2550        call: &Self::Call,2551        info: &DispatchInfoOf<Self::Call>,2552        len: usize,2553    ) -> Result<Self::Pre, TransactionValidityError> {2554        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2555        Ok((self.0, who.clone(), imbalance, fee))2556    }25572558    fn post_dispatch(2559        pre: Self::Pre,2560        info: &DispatchInfoOf<Self::Call>,2561        post_info: &PostDispatchInfoOf<Self::Call>,2562        len: usize,2563        _result: &DispatchResult,2564    ) -> Result<(), TransactionValidityError> {2565        let (tip, who, imbalance, fee) = pre;2566        if let Some(payed) = imbalance {2567            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2568                len as u32, info, post_info, tip,2569            );2570            let refund = fee.saturating_sub(actual_fee);2571            let actual_payment =2572                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2573                    &who, refund,2574                ) {2575                    Ok(refund_imbalance) => {2576                        // The refund cannot be larger than the up front payed max weight.2577                        // `PostDispatchInfo::calc_unspent` guards against such a case.2578                        match payed.offset(refund_imbalance) {2579                            Ok(actual_payment) => actual_payment,2580                            Err(_) => return Err(InvalidTransaction::Payment.into()),2581                        }2582                    }2583                    // We do not recreate the account using the refund. The up front payment2584                    // is gone in that case.2585                    Err(_) => payed,2586                };2587            let imbalances = actual_payment.split(tip);2588            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2589                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2590            );2591        }2592        Ok(())2593    }2594}25952596// #endregion