git.delta.rocks / unique-network / refs/commits / 6994ced8a635

difftreelog

Merge fix

str-mv2020-12-15parent: #8632938.patch.diff
in: master

1 file changed

modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
before · pallets/nft/src/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]23#[cfg(feature = "std")]4pub use std::*;56#[cfg(feature = "std")]7pub use serde::*;89use codec::{Decode, Encode};10pub use frame_support::{11    construct_runtime, decl_event, decl_module, decl_storage, decl_error,12    dispatch::DispatchResult,13    ensure, fail, parameter_types,14    traits::{15        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,16        Randomness, WithdrawReason,17    },18    weights::{19        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},20        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,21        WeightToFeePolynomial,22    },23    IsSubType, StorageValue,24};2526use frame_system::{self as system, ensure_signed, ensure_root};27use sp_runtime::sp_std::prelude::Vec;28use sp_runtime::{29    traits::{30        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SignedExtension, Zero,31    },32    transaction_validity::{33        InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,34    },35    FixedPointOperand, FixedU128,36};37use pallet_contracts::ContractAddressFor;38use sp_runtime::traits::StaticLookup;3940#[cfg(test)]41mod mock;4243#[cfg(test)]44mod tests;4546mod default_weights;4748pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;4950// Structs51// #region5253pub type CollectionId = u32;54pub type TokenId = u32;5556pub type DecimalPoints = u8;5758#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]59#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]60pub enum CollectionMode {61    Invalid,62    NFT,63    // decimal points64    Fungible(DecimalPoints),65    // decimal points66    ReFungible(DecimalPoints),67}6869impl Into<u8> for CollectionMode {70    fn into(self) -> u8 {71        match self {72            CollectionMode::Invalid => 0,73            CollectionMode::NFT => 1,74            CollectionMode::Fungible(_) => 2,75            CollectionMode::ReFungible(_) => 3,76        }77    }78}7980#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]81#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]82pub enum AccessMode {83    Normal,84    WhiteList,85}86impl Default for AccessMode {87    fn default() -> Self {88        Self::Normal89    }90}9192impl Default for CollectionMode {93    fn default() -> Self {94        Self::Invalid95    }96}9798#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]99#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]100pub struct Ownership<AccountId> {101    pub owner: AccountId,102    pub fraction: u128,103}104105#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]106#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]107pub struct CollectionType<AccountId> {108    pub owner: AccountId,109    pub mode: CollectionMode,110    pub access: AccessMode,111    pub decimal_points: DecimalPoints,112    pub name: Vec<u16>,        // 64 include null escape char113    pub description: Vec<u16>, // 256 include null escape char114    pub token_prefix: Vec<u8>, // 16 include null escape char115    pub mint_mode: bool,116    pub offchain_schema: Vec<u8>,117    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender118    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship119    pub limits: CollectionLimits, // Collection private restrictions 120    pub variable_on_chain_schema: Vec<u8>, //121    pub const_on_chain_schema: Vec<u8>, //122}123124#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]125#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]126pub struct NftItemType<AccountId> {127    pub collection: CollectionId,128    pub owner: AccountId,129    pub const_data: Vec<u8>,130    pub variable_data: Vec<u8>,131}132133#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]134#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]135pub struct FungibleItemType<AccountId> {136    pub collection: CollectionId,137    pub owner: AccountId,138    pub value: u128,139}140141#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]142#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]143pub struct ReFungibleItemType<AccountId> {144    pub collection: CollectionId,145    pub owner: Vec<Ownership<AccountId>>,146    pub const_data: Vec<u8>,147    pub variable_data: Vec<u8>,148}149150#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]151#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]152pub struct ApprovePermissions<AccountId> {153    pub approved: AccountId,154    pub amount: u128,155}156157#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]158#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]159pub struct VestingItem<AccountId, Moment> {160    pub sender: AccountId,161    pub recipient: AccountId,162    pub collection_id: CollectionId,163    pub item_id: TokenId,164    pub amount: u64,165    pub vesting_date: Moment,166}167168#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]169#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170pub struct BasketItem<AccountId, BlockNumber> {171    pub address: AccountId,172    pub start_block: BlockNumber,173}174175#[derive(Encode, Decode, Debug, Clone, PartialEq)]176#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]177pub struct CollectionLimits {178    pub account_token_ownership_limit: u32,179    pub sponsored_data_size: u32,180    pub token_limit: u32,181182    // Timeouts for item types in passed blocks183    pub sponsor_transfer_timeout: u32,184}185186impl Default for CollectionLimits {187    fn default() -> CollectionLimits {188        CollectionLimits { 189            account_token_ownership_limit: u32::max_value(), 190            token_limit: u32::max_value(),191            sponsored_data_size: u32::max_value(), 192            sponsor_transfer_timeout: u32::max_value() }193    }194}195196#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]197#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]198pub struct ChainLimits {199    pub collection_numbers_limit: u32,200    pub account_token_ownership_limit: u32,201    pub collections_admins_limit: u64,202    pub custom_data_limit: u32,203204    // Timeouts for item types in passed blocks205    pub nft_sponsor_transfer_timeout: u32,206    pub fungible_sponsor_transfer_timeout: u32,207    pub refungible_sponsor_transfer_timeout: u32,208}209210pub trait WeightInfo {211	fn create_collection() -> Weight;212	fn destroy_collection() -> Weight;213	fn add_to_white_list() -> Weight;214	fn remove_from_white_list() -> Weight;215    fn set_public_access_mode() -> Weight;216    fn set_mint_permission() -> Weight;217    fn change_collection_owner() -> Weight;218    fn add_collection_admin() -> Weight;219    fn remove_collection_admin() -> Weight;220    fn set_collection_sponsor() -> Weight;221    fn confirm_sponsorship() -> Weight;222    fn remove_collection_sponsor() -> Weight;223    fn create_item(s: usize) -> Weight;224    fn burn_item() -> Weight;225    fn transfer() -> Weight;226    fn approve() -> Weight;227    fn transfer_from() -> Weight;228    fn set_offchain_schema() -> Weight;229    fn set_const_on_chain_schema() -> Weight;230    fn set_variable_on_chain_schema() -> Weight;231    fn set_variable_meta_data() -> Weight;232    fn enable_contract_sponsoring() -> Weight;233}234235#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]236#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]237pub struct CreateNftData {238    pub const_data: Vec<u8>,239    pub variable_data: Vec<u8>,240}241242#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]243#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]244pub struct CreateFungibleData {245}246247#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]248#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]249pub struct CreateReFungibleData {250    pub const_data: Vec<u8>,251    pub variable_data: Vec<u8>,252}253254#[derive(Encode, Decode, Debug, Clone, PartialEq)]255#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]256pub enum CreateItemData {257    NFT(CreateNftData),258    Fungible(CreateFungibleData),259    ReFungible(CreateReFungibleData)260}261262impl CreateItemData {263    pub fn len(&self) -> usize {264        let len = match self {265            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),266            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),267            _ => 0268        };269        270        return len;271    }272}273274impl From<CreateNftData> for CreateItemData {275    fn from(item: CreateNftData) -> Self {276        CreateItemData::NFT(item)277    }278}279280impl From<CreateReFungibleData> for CreateItemData {281    fn from(item: CreateReFungibleData) -> Self {282        CreateItemData::ReFungible(item)283    }284}285286impl From<CreateFungibleData> for CreateItemData {287    fn from(item: CreateFungibleData) -> Self {288        CreateItemData::Fungible(item)289    }290}291292293decl_error! {294	/// Error for non-fungible-token module.295	pub enum Error for Module<T: Trait> {296        /// Total collections bound exceeded.297        TotalCollectionsLimitExceeded,298		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.299        CollectionDecimalPointLimitExceeded, 300        /// Collection name can not be longer than 63 char.301        CollectionNameLimitExceeded, 302        /// Collection description can not be longer than 255 char.303        CollectionDescriptionLimitExceeded, 304        /// Token prefix can not be longer than 15 char.305        CollectionTokenPrefixLimitExceeded,306        /// This collection does not exist.307        CollectionNotFound,308        /// Item not exists.309        TokenNotFound,310        /// Arithmetic calculation overflow.311        NumOverflow,       312        /// Account already has admin role.313        AlreadyAdmin,  314        /// You do not own this collection.315        NoPermission,316        /// This address is not set as sponsor, use setCollectionSponsor first.317        ConfirmUnsetSponsorFail,318        /// Collection is not in mint mode.319        PublicMintingNotAllowed,320        /// Sender parameter and item owner must be equal.321        MustBeTokenOwner,322        /// Item balance not enough.323        TokenValueTooLow,324        /// Size of item is too large.325        NftSizeLimitExceeded,326        /// No approve found327        ApproveNotFound,328        /// Requested value more than approved.329        TokenValueNotEnough,330        /// Only approved addresses can call this method.331        ApproveRequired,332        /// Address is not in white list.333        AddresNotInWhiteList,334        /// Number of collection admins bound exceeded.335        CollectionAdminsLimitExceeded,336        /// Owned tokens by a single address bound exceeded.337        AddressOwnershipLimitExceeded,338        /// Length of items properties must be greater than 0.339        EmptyArgument,340        /// const_data exceeded data limit.341        TokenConstDataLimitExceeded,342        /// variable_data exceeded data limit.343        TokenVariableDataLimitExceeded,344        /// Not NFT item data used to mint in NFT collection.345        NotNftDataUsedToMintNftCollectionToken,346        /// Not Fungible item data used to mint in Fungible collection.347        NotFungibleDataUsedToMintFungibleCollectionToken,348        /// Not Re Fungible item data used to mint in Re Fungible collection.349        NotReFungibleDataUsedToMintReFungibleCollectionToken,350        /// Unexpected collection type.351        UnexpectedCollectionType,352        /// Can't store metadata in fungible tokens.353        CantStoreMetadataInFungibleTokens,354        /// Collection token limit exceeded355        CollectionTokenLimitExceeded,356        /// Account token limit exceeded per collection357        AccountTokenLimitExceeded358	}359}360361pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {362    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;363364    /// Weight information for extrinsics in this pallet.365	type WeightInfo: WeightInfo;366}367368#[cfg(feature = "runtime-benchmarks")]369mod benchmarking;370371// #endregion372373decl_storage! {374    trait Store for Module<T: Trait> as Nft {375376        // Private members377        NextCollectionID: CollectionId;378        CreatedCollectionCount: u32;379        ChainVersion: u64;380        ItemListIndex: map hasher(identity) CollectionId => TokenId;381382        // Chain limits struct383        pub ChainLimit get(fn chain_limit) config(): ChainLimits;384385        // Bound counters386        CollectionCount: u32;387        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;388389        // Basic collections390        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;391        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;392        pub WhiteList get(fn white_list): map hasher(identity) CollectionId => Vec<T::AccountId>;393394        /// Balance owner per collection map395        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;396397        /// second parameter: item id + owner account id398        pub ApprovedList get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;399400        /// Item collections401        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;402        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => FungibleItemType<T::AccountId>;403        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;404405        /// Index list406        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;407408        /// Tokens transfer baskets409        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;410        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Vec<BasketItem<T::AccountId, T::BlockNumber>>;411        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;412413        // Contract Sponsorship and Ownership414        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;415        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;416        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;417        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;418    }419    add_extra_genesis {420        build(|config: &GenesisConfig<T>| {421            // Modification of storage422            for (_num, _c) in &config.collection {423                <Module<T>>::init_collection(_c);424            }425426            for (_num, _q, _i) in &config.nft_item_id {427                <Module<T>>::init_nft_token(_i);428            }429430            for (_num, _q, _i) in &config.fungible_item_id {431                <Module<T>>::init_fungible_token(_i);432            }433434            for (_num, _q, _i) in &config.refungible_item_id {435                <Module<T>>::init_refungible_token(_i);436            }437        })438    }439}440441decl_event!(442    pub enum Event<T>443    where444        AccountId = <T as system::Trait>::AccountId,445    {446        /// New collection was created447        /// 448        /// # Arguments449        /// 450        /// * collection_id: Globally unique identifier of newly created collection.451        /// 452        /// * mode: [CollectionMode] converted into u8.453        /// 454        /// * account_id: Collection owner.455        Created(CollectionId, u8, AccountId),456457        /// New item was created.458        /// 459        /// # Arguments460        /// 461        /// * collection_id: Id of the collection where item was created.462        /// 463        /// * item_id: Id of an item. Unique within the collection.464        ItemCreated(CollectionId, TokenId),465466        /// Collection item was burned.467        /// 468        /// # Arguments469        /// 470        /// collection_id.471        /// 472        /// item_id: Identifier of burned NFT.473        ItemDestroyed(CollectionId, TokenId),474    }475);476477decl_module! {478    pub struct Module<T: Trait> for enum Call where origin: T::Origin {479480        fn deposit_event() = default;481        type Error = Error<T>;482483        fn on_initialize(now: T::BlockNumber) -> Weight {484485            if ChainVersion::get() < 2486            {487                let value = NextCollectionID::get();488                CreatedCollectionCount::put(value);489                ChainVersion::put(2);490            }491492            0493        }494495        /// 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.496        /// 497        /// # Permissions498        /// 499        /// * Anyone.500        /// 501        /// # Arguments502        /// 503        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.504        /// 505        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.506        /// 507        /// * token_prefix: UTF-8 string with token prefix.508        /// 509        /// * mode: [CollectionMode] collection type and type dependent data.510        // returns collection ID511        #[weight = T::WeightInfo::create_collection()]512        pub fn create_collection(origin,513                                 collection_name: Vec<u16>,514                                 collection_description: Vec<u16>,515                                 token_prefix: Vec<u8>,516                                 mode: CollectionMode) -> DispatchResult {517518            // Anyone can create a collection519            let who = ensure_signed(origin)?;520521            let decimal_points = match mode {522                CollectionMode::Fungible(points) => points,523                CollectionMode::ReFungible(points) => points,524                _ => 0525            };526527            // bound Total number of collections528            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);529530            // check params531            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);532533            let mut name = collection_name.to_vec();534            name.push(0);535            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);536537            let mut description = collection_description.to_vec();538            description.push(0);539            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);540541            let mut prefix = token_prefix.to_vec();542            prefix.push(0);543            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);544545            // Generate next collection ID546            let next_id = CreatedCollectionCount::get()547                .checked_add(1)548                .ok_or(Error::<T>::NumOverflow)?;549550            // bound counter551            let total = CollectionCount::get()552                .checked_add(1)553                .ok_or(Error::<T>::NumOverflow)?;554555            CreatedCollectionCount::put(next_id);556            CollectionCount::put(total);557558            // Create new collection559            let new_collection = CollectionType {560                owner: who.clone(),561                name: name,562                mode: mode.clone(),563                mint_mode: false,564                access: AccessMode::Normal,565                description: description,566                decimal_points: decimal_points,567                token_prefix: prefix,568                offchain_schema: Vec::new(),569                sponsor: T::AccountId::default(),570                unconfirmed_sponsor: T::AccountId::default(),571                variable_on_chain_schema: Vec::new(),572                const_on_chain_schema: Vec::new(),573                limits: CollectionLimits::default(),574            };575576            // Add new collection to map577            <Collection<T>>::insert(next_id, new_collection);578579            // call event580            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));581582            Ok(())583        }584585        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.586        /// 587        /// # Permissions588        /// 589        /// * Collection Owner.590        /// 591        /// # Arguments592        /// 593        /// * collection_id: collection to destroy.594        #[weight = T::WeightInfo::destroy_collection()]595        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {596597            let sender = ensure_signed(origin)?;598            Self::check_owner_permissions(collection_id, sender)?;599600            <AddressTokens<T>>::remove_prefix(collection_id);601            <ApprovedList<T>>::remove_prefix(collection_id);602            <Balance<T>>::remove_prefix(collection_id);603            <ItemListIndex>::remove(collection_id);604            <AdminList<T>>::remove(collection_id);605            <Collection<T>>::remove(collection_id);606            <WhiteList<T>>::remove(collection_id);607608            <NftItemList<T>>::remove_prefix(collection_id);609            <FungibleItemList<T>>::remove_prefix(collection_id);610            <ReFungibleItemList<T>>::remove_prefix(collection_id);611612            <NftTransferBasket<T>>::remove_prefix(collection_id);613            <FungibleTransferBasket<T>>::remove_prefix(collection_id);614            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);615616            if CollectionCount::get() > 0617            {618                // bound couter619                let total = CollectionCount::get()620                    .checked_sub(1)621                    .ok_or(Error::<T>::NumOverflow)?;622623                CollectionCount::put(total);624            }625626            Ok(())627        }628629        /// Add an address to white list.630        /// 631        /// # Permissions632        /// 633        /// * Collection Owner634        /// * Collection Admin635        /// 636        /// # Arguments637        /// 638        /// * collection_id.639        /// 640        /// * address.641        #[weight = T::WeightInfo::add_to_white_list()]642        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{643644            let sender = ensure_signed(origin)?;645            Self::check_owner_or_admin_permissions(collection_id, sender)?;646647            let mut white_list_collection: Vec<T::AccountId>;648            if <WhiteList<T>>::contains_key(collection_id) {649                white_list_collection = <WhiteList<T>>::get(collection_id);650                if !white_list_collection.contains(&address.clone())651                {652                    white_list_collection.push(address.clone());653                }654            }655            else {656                white_list_collection = Vec::new();657                white_list_collection.push(address.clone());658            }659660            <WhiteList<T>>::insert(collection_id, white_list_collection);661            Ok(())662        }663664        /// Remove an address from white list.665        /// 666        /// # Permissions667        /// 668        /// * Collection Owner669        /// * Collection Admin670        /// 671        /// # Arguments672        /// 673        /// * collection_id.674        /// 675        /// * address.676        #[weight = T::WeightInfo::remove_from_white_list()]677        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{678679            let sender = ensure_signed(origin)?;680            Self::check_owner_or_admin_permissions(collection_id, sender)?;681682            if <WhiteList<T>>::contains_key(collection_id) {683                let mut white_list_collection = <WhiteList<T>>::get(collection_id);684                if white_list_collection.contains(&address.clone())685                {686                    white_list_collection.retain(|i| *i != address.clone());687                    <WhiteList<T>>::insert(collection_id, white_list_collection);688                }689            }690691            Ok(())692        }693694        /// Toggle between normal and white list access for the methods with access for `Anyone`.695        /// 696        /// # Permissions697        /// 698        /// * Collection Owner.699        /// 700        /// # Arguments701        /// 702        /// * collection_id.703        /// 704        /// * mode: [AccessMode]705        #[weight = T::WeightInfo::set_public_access_mode()]706        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult707        {708            let sender = ensure_signed(origin)?;709710            Self::check_owner_permissions(collection_id, sender)?;711            let mut target_collection = <Collection<T>>::get(collection_id);712            target_collection.access = mode;713            <Collection<T>>::insert(collection_id, target_collection);714715            Ok(())716        }717718        /// Allows Anyone to create tokens if:719        /// * White List is enabled, and720        /// * Address is added to white list, and721        /// * This method was called with True parameter722        /// 723        /// # Permissions724        /// * Collection Owner725        ///726        /// # Arguments727        /// 728        /// * collection_id.729        /// 730        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.731        #[weight = T::WeightInfo::set_mint_permission()]732        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult733        {734            let sender = ensure_signed(origin)?;735736            Self::check_owner_permissions(collection_id, sender)?;737            let mut target_collection = <Collection<T>>::get(collection_id);738            target_collection.mint_mode = mint_permission;739            <Collection<T>>::insert(collection_id, target_collection);740741            Ok(())742        }743744        /// Change the owner of the collection.745        /// 746        /// # Permissions747        /// 748        /// * Collection Owner.749        /// 750        /// # Arguments751        /// 752        /// * collection_id.753        /// 754        /// * new_owner.755        #[weight = T::WeightInfo::change_collection_owner()]756        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {757758            let sender = ensure_signed(origin)?;759            Self::check_owner_permissions(collection_id, sender)?;760            let mut target_collection = <Collection<T>>::get(collection_id);761            target_collection.owner = new_owner;762            <Collection<T>>::insert(collection_id, target_collection);763764            Ok(())765        }766767        /// Adds an admin of the Collection.768        /// 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. 769        /// 770        /// # Permissions771        /// 772        /// * Collection Owner.773        /// * Collection Admin.774        /// 775        /// # Arguments776        /// 777        /// * collection_id: ID of the Collection to add admin for.778        /// 779        /// * new_admin_id: Address of new admin to add.780        #[weight = T::WeightInfo::add_collection_admin()]781        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {782783            let sender = ensure_signed(origin)?;784            Self::check_owner_or_admin_permissions(collection_id, sender)?;785            let mut admin_arr: Vec<T::AccountId> = Vec::new();786787            if <AdminList<T>>::contains_key(collection_id)788            {789                admin_arr = <AdminList<T>>::get(collection_id);790                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);791            }792793            // Number of collection admins794            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);795796            admin_arr.push(new_admin_id);797            <AdminList<T>>::insert(collection_id, admin_arr);798799            Ok(())800        }801802        /// 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.803        ///804        /// # Permissions805        /// 806        /// * Collection Owner.807        /// * Collection Admin.808        /// 809        /// # Arguments810        /// 811        /// * collection_id: ID of the Collection to remove admin for.812        /// 813        /// * account_id: Address of admin to remove.814        #[weight = T::WeightInfo::remove_collection_admin()]815        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {816817            let sender = ensure_signed(origin)?;818            Self::check_owner_or_admin_permissions(collection_id, sender)?;819820            if <AdminList<T>>::contains_key(collection_id)821            {822                let mut admin_arr = <AdminList<T>>::get(collection_id);823                admin_arr.retain(|i| *i != account_id);824                <AdminList<T>>::insert(collection_id, admin_arr);825            }826827            Ok(())828        }829830        /// # Permissions831        /// 832        /// * Collection Owner833        /// 834        /// # Arguments835        /// 836        /// * collection_id.837        /// 838        /// * new_sponsor.839        #[weight = T::WeightInfo::set_collection_sponsor()]840        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {841842            let sender = ensure_signed(origin)?;843            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);844845            let mut target_collection = <Collection<T>>::get(collection_id);846            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);847848            target_collection.unconfirmed_sponsor = new_sponsor;849            <Collection<T>>::insert(collection_id, target_collection);850851            Ok(())852        }853854        /// # Permissions855        /// 856        /// * Sponsor.857        /// 858        /// # Arguments859        /// 860        /// * collection_id.861        #[weight = T::WeightInfo::confirm_sponsorship()]862        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {863864            let sender = ensure_signed(origin)?;865            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);866867            let mut target_collection = <Collection<T>>::get(collection_id);868            ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);869870            target_collection.sponsor = target_collection.unconfirmed_sponsor;871            target_collection.unconfirmed_sponsor = T::AccountId::default();872            <Collection<T>>::insert(collection_id, target_collection);873874            Ok(())875        }876877        /// Switch back to pay-per-own-transaction model.878        ///879        /// # Permissions880        ///881        /// * Collection owner.882        /// 883        /// # Arguments884        /// 885        /// * collection_id.886        #[weight = T::WeightInfo::remove_collection_sponsor()]887        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {888889            let sender = ensure_signed(origin)?;890            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);891892            let mut target_collection = <Collection<T>>::get(collection_id);893            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);894895            target_collection.sponsor = T::AccountId::default();896            <Collection<T>>::insert(collection_id, target_collection);897898            Ok(())899        }900901        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.902        /// 903        /// # Permissions904        /// 905        /// * Collection Owner.906        /// * Collection Admin.907        /// * Anyone if908        ///     * White List is enabled, and909        ///     * Address is added to white list, and910        ///     * MintPermission is enabled (see SetMintPermission method)911        /// 912        /// # Arguments913        /// 914        /// * collection_id: ID of the collection.915        /// 916        /// * owner: Address, initial owner of the NFT.917        ///918        /// * data: Token data to store on chain.919        // #[weight =920        // (130_000_000 as Weight)921        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))922        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))923        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]924925        #[weight = T::WeightInfo::create_item(data.len())]926        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {927928            let sender = ensure_signed(origin)?;929930            Self::collection_exists(collection_id)?;931932            let target_collection = <Collection<T>>::get(collection_id);933934            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;935            Self::validate_create_item_args(&target_collection, &data)?;936            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;937938            Ok(())939        }940941        /// This method creates multiple instances of NFT Collection created with CreateCollection method.942        /// 943        /// # Permissions944        /// 945        /// * Collection Owner.946        /// * Collection Admin.947        /// * Anyone if948        ///     * White List is enabled, and949        ///     * Address is added to white list, and950        ///     * MintPermission is enabled (see SetMintPermission method)951        /// 952        /// # Arguments953        /// 954        /// * collection_id: ID of the collection.955        /// 956        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].957        /// 958        /// * owner: Address, initial owner of the NFT.959        #[weight = T::WeightInfo::create_item(items_data.into_iter()960                               .map(|data| { data.len() })961                               .sum())]962        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {963964            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);965            let sender = ensure_signed(origin)?;966967            Self::collection_exists(collection_id)?;968            let target_collection = <Collection<T>>::get(collection_id);969970            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;971972            for data in &items_data {973                Self::validate_create_item_args(&target_collection, data)?;974            }975            for data in &items_data {976                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;977            }978979            Ok(())980        }981982        /// Destroys a concrete instance of NFT.983        /// 984        /// # Permissions985        /// 986        /// * Collection Owner.987        /// * Collection Admin.988        /// * Current NFT Owner.989        /// 990        /// # Arguments991        /// 992        /// * collection_id: ID of the collection.993        /// 994        /// * item_id: ID of NFT to burn.995        #[weight = T::WeightInfo::burn_item()]996        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {997998            let sender = ensure_signed(origin)?;999            Self::collection_exists(collection_id)?;10001001            // Transfer permissions check1002            let target_collection = <Collection<T>>::get(collection_id);1003            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1004                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1005                Error::<T>::NoPermission);10061007            if target_collection.access == AccessMode::WhiteList {1008                Self::check_white_list(collection_id, &sender)?;1009            }10101011            match target_collection.mode1012            {1013                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1014                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,1015                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1016                _ => ()1017            };10181019            // call event1020            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10211022            Ok(())1023        }10241025        /// Change ownership of the token.1026        /// 1027        /// # Permissions1028        /// 1029        /// * Collection Owner1030        /// * Collection Admin1031        /// * Current NFT owner1032        ///1033        /// # Arguments1034        /// 1035        /// * recipient: Address of token recipient.1036        /// 1037        /// * collection_id.1038        /// 1039        /// * item_id: ID of the item1040        ///     * Non-Fungible Mode: Required.1041        ///     * Fungible Mode: Ignored.1042        ///     * Re-Fungible Mode: Required.1043        /// 1044        /// * value: Amount to transfer.1045        ///     * Non-Fungible Mode: Ignored1046        ///     * Fungible Mode: Must specify transferred amount1047        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1048        #[weight = T::WeightInfo::transfer()]1049        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10501051            let sender = ensure_signed(origin)?;1052            let target_collection = <Collection<T>>::get(collection_id);10531054            // Limits check1055            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;10561057            // Transfer permissions check1058            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1059                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1060                Error::<T>::NoPermission);10611062            if target_collection.access == AccessMode::WhiteList {1063                Self::check_white_list(collection_id, &sender)?;1064                Self::check_white_list(collection_id, &recipient)?;1065            }10661067            match target_collection.mode1068            {1069                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1070                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1071                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1072                _ => ()1073            };10741075            Ok(())1076        }10771078        /// Set, change, or remove approved address to transfer the ownership of the NFT.1079        /// 1080        /// # Permissions1081        /// 1082        /// * Collection Owner1083        /// * Collection Admin1084        /// * Current NFT owner1085        /// 1086        /// # Arguments1087        /// 1088        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1089        /// 1090        /// * collection_id.1091        /// 1092        /// * item_id: ID of the item.1093        #[weight = T::WeightInfo::approve()]1094        pub fn approve(origin, approved: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {10951096            let sender = ensure_signed(origin)?;10971098            // Transfer permissions check1099            let target_collection = <Collection<T>>::get(collection_id);1100            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1101                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1102                Error::<T>::NoPermission);11031104            if target_collection.access == AccessMode::WhiteList {1105                Self::check_white_list(collection_id, &sender)?;1106                Self::check_white_list(collection_id, &approved)?;1107            }11081109            // amount param stub1110            let amount = 100000000;11111112            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1113            if list_exists {11141115                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1116                let item_contains = list.iter().any(|i| i.approved == approved);11171118                if !item_contains {1119                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1120                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1121                }1122            } else {11231124                let mut list = Vec::new();1125                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1126                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1127            }11281129            Ok(())1130        }1131        1132        /// 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.1133        /// 1134        /// # Permissions1135        /// * Collection Owner1136        /// * Collection Admin1137        /// * Current NFT owner1138        /// * Address approved by current NFT owner1139        /// 1140        /// # Arguments1141        /// 1142        /// * from: Address that owns token.1143        /// 1144        /// * recipient: Address of token recipient.1145        /// 1146        /// * collection_id.1147        /// 1148        /// * item_id: ID of the item.1149        /// 1150        /// * value: Amount to transfer.1151        #[weight = T::WeightInfo::transfer_from()]1152        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11531154            let sender = ensure_signed(origin)?;1155            let mut appoved_transfer = false;11561157            // Check approve1158            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1159                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1160                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1161                if opt_item.is_some()1162                {1163                    appoved_transfer = true;1164                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1165                }1166            }11671168            let target_collection = <Collection<T>>::get(collection_id);11691170            // Limits check1171            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11721173            // Transfer permissions check         1174            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1175            Error::<T>::NoPermission);11761177            if target_collection.access == AccessMode::WhiteList {1178                Self::check_white_list(collection_id, &sender)?;1179                Self::check_white_list(collection_id, &recipient)?;1180            }11811182            // remove approve1183            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1184                .into_iter().filter(|i| i.approved != sender.clone()).collect();1185            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);118611871188            match target_collection.mode1189            {1190                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1191                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1192                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1193                _ => ()1194            };11951196            Ok(())1197        }11981199        ///1200        #[weight = 0]1201        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {12021203            // let no_perm_mes = "You do not have permissions to modify this collection";1204            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1205            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1206            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);12071208            // // on_nft_received  call12091210            // Self::transfer(origin, collection_id, item_id, new_owner)?;12111212            Ok(())1213        }12141215        /// Set off-chain data schema.1216        /// 1217        /// # Permissions1218        /// 1219        /// * Collection Owner1220        /// * Collection Admin1221        /// 1222        /// # Arguments1223        /// 1224        /// * collection_id.1225        /// 1226        /// * schema: String representing the offchain data schema.1227        #[weight = T::WeightInfo::set_variable_meta_data()]1228        pub fn set_variable_meta_data (1229            origin,1230            collection_id: CollectionId,1231            item_id: TokenId,1232            data: Vec<u8>1233        ) -> DispatchResult {1234            let sender = ensure_signed(origin)?;1235            1236            Self::collection_exists(collection_id)?;1237            1238            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12391240            // Modify permissions check1241            let target_collection = <Collection<T>>::get(collection_id);1242            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1243                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1244                Error::<T>::NoPermission);12451246            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12471248            match target_collection.mode1249            {1250                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1251                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1252                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1253                _ => fail!(Error::<T>::UnexpectedCollectionType)1254            };12551256            Ok(())1257        }1258        12591260        /// Set off-chain data schema.1261        /// 1262        /// # Permissions1263        /// 1264        /// * Collection Owner1265        /// * Collection Admin1266        /// 1267        /// # Arguments1268        /// 1269        /// * collection_id.1270        /// 1271        /// * schema: String representing the offchain data schema.1272        #[weight = T::WeightInfo::set_offchain_schema()]1273        pub fn set_offchain_schema(1274            origin,1275            collection_id: CollectionId,1276            schema: Vec<u8>1277        ) -> DispatchResult {1278            let sender = ensure_signed(origin)?;1279            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12801281            let mut target_collection = <Collection<T>>::get(collection_id);1282            target_collection.offchain_schema = schema;1283            <Collection<T>>::insert(collection_id, target_collection);12841285            Ok(())1286        }12871288        /// Set const on-chain data schema.1289        /// 1290        /// # Permissions1291        /// 1292        /// * Collection Owner1293        /// * Collection Admin1294        /// 1295        /// # Arguments1296        /// 1297        /// * collection_id.1298        /// 1299        /// * schema: String representing the const on-chain data schema.1300        #[weight = T::WeightInfo::set_const_on_chain_schema()]1301        pub fn set_const_on_chain_schema (1302            origin,1303            collection_id: CollectionId,1304            schema: Vec<u8>1305        ) -> DispatchResult {1306            let sender = ensure_signed(origin)?;1307            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13081309            let mut target_collection = <Collection<T>>::get(collection_id);1310            target_collection.const_on_chain_schema = schema;1311            <Collection<T>>::insert(collection_id, target_collection);13121313            Ok(())1314        }13151316        /// Set variable on-chain data schema.1317        /// 1318        /// # Permissions1319        /// 1320        /// * Collection Owner1321        /// * Collection Admin1322        /// 1323        /// # Arguments1324        /// 1325        /// * collection_id.1326        /// 1327        /// * schema: String representing the variable on-chain data schema.1328        #[weight = T::WeightInfo::set_const_on_chain_schema()]1329        pub fn set_variable_on_chain_schema (1330            origin,1331            collection_id: CollectionId,1332            schema: Vec<u8>1333        ) -> DispatchResult {1334            let sender = ensure_signed(origin)?;1335            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13361337            let mut target_collection = <Collection<T>>::get(collection_id);1338            target_collection.variable_on_chain_schema = schema;1339            <Collection<T>>::insert(collection_id, target_collection);13401341            Ok(())1342        }13431344        // Sudo permissions function1345        #[weight = 0]1346        pub fn set_chain_limits(1347            origin,1348            limits: ChainLimits1349        ) -> DispatchResult {1350            ensure_root(origin)?;1351            <ChainLimit>::put(limits);1352            Ok(())1353        }13541355        /// Enable smart contract self-sponsoring.1356        /// 1357        /// # Permissions1358        /// 1359        /// * Contract Owner1360        /// 1361        /// # Arguments1362        /// 1363        /// * contract address1364        /// * enable flag1365        /// 1366        #[weight = T::WeightInfo::enable_contract_sponsoring()]1367        pub fn enable_contract_sponsoring(1368            origin,1369            contract_address: T::AccountId,1370            enable: bool1371        ) -> DispatchResult {13721373            let sender = ensure_signed(origin)?;13741375            #[cfg(feature = "runtime-benchmarks")]1376            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13771378            let mut is_owner = false;1379            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1380                let owner = <ContractOwner<T>>::get(&contract_address);1381                is_owner = sender == owner;1382            }1383            ensure!(is_owner, Error::<T>::NoPermission);13841385            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1386            Ok(())1387        }13881389        /// Set the rate limit for contract sponsoring to specified number of blocks.1390        /// 1391        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1392        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1393        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1394        /// from contract endowment if there are at least B blocks between such transactions. 1395        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1396        /// 1397        /// # Permissions1398        /// 1399        /// * Contract Owner1400        /// 1401        /// # Arguments1402        /// 1403        /// -`contract_address`: Address of the contract to sponsor1404        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1405        /// 1406        #[weight = 0]1407        pub fn set_contract_sponsoring_rate_limit(1408            origin,1409            contract_address: T::AccountId,1410            rate_limit: T::BlockNumber1411        ) -> DispatchResult {1412            let sender = ensure_signed(origin)?;1413            let mut is_owner = false;1414            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1415                let owner = <ContractOwner<T>>::get(&contract_address);1416                is_owner = sender == owner;1417            }1418            ensure!(is_owner, Error::<T>::NoPermission);14191420            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1421            Ok(())1422        }14231424        #[weight = 0]1425        pub fn set_collection_limits(1426            origin,1427            collection_id: u32,1428            limits: CollectionLimits,1429        ) -> DispatchResult {1430            let sender = ensure_signed(origin)?;1431            Self::check_owner_permissions(collection_id, sender.clone())?;1432            let mut target_collection = <Collection<T>>::get(collection_id);1433            let chain_limits = ChainLimit::get();1434            let climits = target_collection.limits;14351436            // token_limit   check  prev1437            ensure!(climits.token_limit > limits.token_limit && 1438                climits.token_limit <= chain_limits.account_token_ownership_limit, 1439                Error::<T>::AccountTokenLimitExceeded);14401441            target_collection.limits = limits;1442            <Collection<T>>::insert(collection_id, target_collection);14431444            Ok(())1445        } 1446    }1447}14481449impl<T: Trait> Module<T> {14501451    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {14521453        if !Self::is_owner_or_admin_permissions(collection_id, recipient.clone()) {14541455            // check token limit and account token limit1456            let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1457            ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1458        }14591460        Ok(())1461    }14621463    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {14641465        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {14661467            // check token limit and account token limit1468            let total_items: u32 = ItemListIndex::get(collection_id);1469            let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1470            ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1471            ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1472            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1473            Self::check_white_list(collection_id, owner)?;1474            Self::check_white_list(collection_id, sender)?;1475        }14761477        Ok(())1478    }14791480    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1481        match target_collection.mode1482        {1483            CollectionMode::NFT => {1484                if let CreateItemData::NFT(data) = data {1485                    // check sizes1486                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1487                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1488                } else {1489                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1490                }1491            },1492            CollectionMode::Fungible(_) => {1493                if let CreateItemData::Fungible(_) = data {1494                } else {1495                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1496                }1497            },1498            CollectionMode::ReFungible(_) => {1499                if let CreateItemData::ReFungible(data) = data {15001501                    // check sizes1502                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1503                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1504                } else {1505                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1506                }1507            },1508            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1509        };15101511        Ok(())1512    }15131514    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1515        match data1516        {1517            CreateItemData::NFT(data) => {1518                let item = NftItemType {1519                    collection: collection_id,1520                    owner,1521                    const_data: data.const_data,1522                    variable_data: data.variable_data1523                };15241525                Self::add_nft_item(item)?;1526            },1527            CreateItemData::Fungible(_) => {1528                let item = FungibleItemType {1529                    collection: collection_id,1530                    owner,1531                    value: (10 as u128).pow(collection.decimal_points as u32)1532                };15331534                Self::add_fungible_item(item)?;1535            },1536            CreateItemData::ReFungible(data) => {1537                let mut owner_list = Vec::new();1538                let value = (10 as u128).pow(collection.decimal_points as u32);1539                owner_list.push(Ownership {owner: owner.clone(), fraction: value});15401541                let item = ReFungibleItemType {1542                    collection: collection_id,1543                    owner: owner_list,1544                    const_data: data.const_data,1545                    variable_data: data.variable_data1546                };15471548                Self::add_refungible_item(item)?;1549            }1550        };15511552        // call event1553        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));15541555        Ok(())1556    }15571558    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1559        let current_index = <ItemListIndex>::get(item.collection)1560            .checked_add(1)1561            .ok_or(Error::<T>::NumOverflow)?;1562        let itemcopy = item.clone();1563        let owner = item.owner.clone();15641565        Self::add_token_index(item.collection, current_index, owner.clone())?;15661567        <ItemListIndex>::insert(item.collection, current_index);1568        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15691570        // Add current block1571        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1572        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1573        1574        // Update balance1575        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1576            .checked_add(item.value)1577            .ok_or(Error::<T>::NumOverflow)?;1578        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15791580        Ok(())1581    }15821583    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1584        let current_index = <ItemListIndex>::get(item.collection)1585            .checked_add(1)1586            .ok_or(Error::<T>::NumOverflow)?;1587        let itemcopy = item.clone();15881589        let value = item.owner.first().unwrap().fraction;1590        let owner = item.owner.first().unwrap().owner.clone();15911592        Self::add_token_index(item.collection, current_index, owner.clone())?;15931594        <ItemListIndex>::insert(item.collection, current_index);1595        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15961597        // Add current block1598        let block_number: T::BlockNumber = 0.into();1599        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);16001601        // Update balance1602        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1603            .checked_add(value)1604            .ok_or(Error::<T>::NumOverflow)?;1605        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);16061607        Ok(())1608    }16091610    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1611        let current_index = <ItemListIndex>::get(item.collection)1612            .checked_add(1)1613            .ok_or(Error::<T>::NumOverflow)?;16141615        let item_owner = item.owner.clone();1616        let collection_id = item.collection.clone();1617        Self::add_token_index(collection_id, current_index, item.owner.clone())?;16181619        <ItemListIndex>::insert(collection_id, current_index);1620        <NftItemList<T>>::insert(collection_id, current_index, item);16211622        // Add current block1623        let block_number: T::BlockNumber = 0.into();1624        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);16251626        // Update balance1627        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1628            .checked_add(1)1629            .ok_or(Error::<T>::NumOverflow)?;1630        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);16311632        Ok(())1633    }16341635    fn burn_refungible_item(1636        collection_id: CollectionId,1637        item_id: TokenId,1638        owner: T::AccountId,1639    ) -> DispatchResult {1640        ensure!(1641            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1642            Error::<T>::TokenNotFound1643        );1644        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1645        let item = collection1646            .owner1647            .iter()1648            .filter(|&i| i.owner == owner)1649            .next()1650            .unwrap();1651        Self::remove_token_index(collection_id, item_id, owner.clone())?;16521653        // remove approve list1654        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));16551656        // update balance1657        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1658            .checked_sub(item.fraction)1659            .ok_or(Error::<T>::NumOverflow)?;1660        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);16611662        <ReFungibleItemList<T>>::remove(collection_id, item_id);16631664        Ok(())1665    }16661667    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1668        ensure!(1669            <NftItemList<T>>::contains_key(collection_id, item_id),1670            Error::<T>::TokenNotFound1671        );1672        let item = <NftItemList<T>>::get(collection_id, item_id);1673        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16741675        // remove approve list1676        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16771678        // update balance1679        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1680            .checked_sub(1)1681            .ok_or(Error::<T>::NumOverflow)?;1682        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1683        <NftItemList<T>>::remove(collection_id, item_id);16841685        Ok(())1686    }16871688    fn burn_fungible_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1689        ensure!(1690            <FungibleItemList<T>>::contains_key(collection_id, item_id),1691            Error::<T>::TokenNotFound1692        );1693        let item = <FungibleItemList<T>>::get(collection_id, item_id);1694        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16951696        // remove approve list1697        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16981699        // update balance1700        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1701            .checked_sub(item.value)1702            .ok_or(Error::<T>::NumOverflow)?;1703        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17041705        <FungibleItemList<T>>::remove(collection_id, item_id);17061707        Ok(())1708    }17091710    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1711        ensure!(1712            <Collection<T>>::contains_key(collection_id),1713            Error::<T>::CollectionNotFound1714        );1715        Ok(())1716    }17171718    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1719        Self::collection_exists(collection_id)?;17201721        let target_collection = <Collection<T>>::get(collection_id);1722        ensure!(1723            subject == target_collection.owner,1724            Error::<T>::NoPermission1725        );17261727        Ok(())1728    }17291730    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1731        let target_collection = <Collection<T>>::get(collection_id);1732        let mut result: bool = subject == target_collection.owner;1733        let exists = <AdminList<T>>::contains_key(collection_id);17341735        if !result & exists {1736            if <AdminList<T>>::get(collection_id).contains(&subject) {1737                result = true1738            }1739        }17401741        result1742    }17431744    fn check_owner_or_admin_permissions(1745        collection_id: CollectionId,1746        subject: T::AccountId,1747    ) -> DispatchResult {1748        Self::collection_exists(collection_id)?;1749        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());17501751        ensure!(1752            result,1753            Error::<T>::NoPermission1754        );1755        Ok(())1756    }17571758    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1759        let target_collection = <Collection<T>>::get(collection_id);17601761        match target_collection.mode {1762            CollectionMode::NFT => {1763                <NftItemList<T>>::get(collection_id, item_id).owner == subject1764            }1765            CollectionMode::Fungible(_) => {1766                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1767            }1768            CollectionMode::ReFungible(_) => {1769                <ReFungibleItemList<T>>::get(collection_id, item_id)1770                    .owner1771                    .iter()1772                    .any(|i| i.owner == subject)1773            }1774            CollectionMode::Invalid => false,1775        }1776    }17771778    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1779        let mes = Error::<T>::AddresNotInWhiteList;1780        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1781        let wl = <WhiteList<T>>::get(collection_id);1782        ensure!(wl.contains(address), mes);17831784        Ok(())1785    }17861787    fn transfer_fungible(1788        collection_id: CollectionId,1789        item_id: TokenId,1790        value: u128,1791        owner: T::AccountId,1792        new_owner: T::AccountId,1793    ) -> DispatchResult {1794        ensure!(1795            <FungibleItemList<T>>::contains_key(collection_id, item_id),1796            Error::<T>::TokenNotFound1797        );17981799        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1800        let amount = full_item.value;18011802        ensure!(amount >= value, Error::<T>::TokenValueTooLow);18031804        // update balance1805        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1806            .checked_sub(value)1807            .ok_or(Error::<T>::NumOverflow)?;1808        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);18091810        let mut new_owner_account_id = 0;1811        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1812        if new_owner_items.len() > 0 {1813            new_owner_account_id = new_owner_items[0];1814        }18151816        // transfer1817        if amount == value && new_owner_account_id == 0 {1818            // change owner1819            // new owner do not have account1820            let mut new_full_item = full_item.clone();1821            new_full_item.owner = new_owner.clone();1822            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18231824            // update balance1825            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1826                .checked_add(value)1827                .ok_or(Error::<T>::NumOverflow)?;1828            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18291830            // update index collection1831            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1832        } else {1833            let mut new_full_item = full_item.clone();1834            new_full_item.value -= value;18351836            // separate amount1837            if new_owner_account_id > 0 {1838                // new owner has account1839                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1840                item.value += value;18411842                // update balance1843                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1844                    .checked_add(value)1845                    .ok_or(Error::<T>::NumOverflow)?;1846                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18471848                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1849            } else {1850                // new owner do not have account1851                let item = FungibleItemType {1852                    collection: collection_id,1853                    owner: new_owner.clone(),1854                    value1855                };18561857                Self::add_fungible_item(item)?;1858            }18591860            if amount == value {1861                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;18621863                // remove approve list1864                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1865                <FungibleItemList<T>>::remove(collection_id, item_id);1866            }18671868            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1869        }18701871        Ok(())1872    }18731874    fn transfer_refungible(1875        collection_id: CollectionId,1876        item_id: TokenId,1877        value: u128,1878        owner: T::AccountId,1879        new_owner: T::AccountId,1880    ) -> DispatchResult {1881        ensure!(1882            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1883            Error::<T>::TokenNotFound1884        );18851886        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1887        let item = full_item1888            .owner1889            .iter()1890            .filter(|i| i.owner == owner)1891            .next()1892            .ok_or(Error::<T>::NumOverflow)?;1893        let amount = item.fraction;18941895        ensure!(amount >= value, Error::<T>::TokenValueTooLow);18961897        // update balance1898        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1899            .checked_sub(value)1900            .ok_or(Error::<T>::NumOverflow)?;1901        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19021903        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1904            .checked_add(value)1905            .ok_or(Error::<T>::NumOverflow)?;1906        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19071908        let old_owner = item.owner.clone();1909        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19101911        // transfer1912        if amount == value && !new_owner_has_account {1913            // change owner1914            // new owner do not have account1915            let mut new_full_item = full_item.clone();1916            new_full_item1917                .owner1918                .iter_mut()1919                .find(|i| i.owner == owner)1920                .unwrap()1921                .owner = new_owner.clone();1922            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19231924            // update index collection1925            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1926        } else {1927            let mut new_full_item = full_item.clone();1928            new_full_item1929                .owner1930                .iter_mut()1931                .find(|i| i.owner == owner)1932                .unwrap()1933                .fraction -= value;19341935            // separate amount1936            if new_owner_has_account {1937                // new owner has account1938                new_full_item1939                    .owner1940                    .iter_mut()1941                    .find(|i| i.owner == new_owner)1942                    .unwrap()1943                    .fraction += value;1944            } else {1945                // new owner do not have account1946                new_full_item.owner.push(Ownership {1947                    owner: new_owner.clone(),1948                    fraction: value,1949                });1950                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1951            }19521953            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1954        }19551956        Ok(())1957    }19581959    fn transfer_nft(1960        collection_id: CollectionId,1961        item_id: TokenId,1962        sender: T::AccountId,1963        new_owner: T::AccountId,1964    ) -> DispatchResult {1965        ensure!(1966            <NftItemList<T>>::contains_key(collection_id, item_id),1967            Error::<T>::TokenNotFound1968        );19691970        let mut item = <NftItemList<T>>::get(collection_id, item_id);19711972        ensure!(1973            sender == item.owner,1974            Error::<T>::MustBeTokenOwner1975        );19761977        // update balance1978        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1979            .checked_sub(1)1980            .ok_or(Error::<T>::NumOverflow)?;1981        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19821983        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1984            .checked_add(1)1985            .ok_or(Error::<T>::NumOverflow)?;1986        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19871988        // change owner1989        let old_owner = item.owner.clone();1990        item.owner = new_owner.clone();1991        <NftItemList<T>>::insert(collection_id, item_id, item);19921993        // update index collection1994        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;19951996        // reset approved list1997        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1998        Ok(())1999    }2000    2001    fn item_exists(2002        collection_id: CollectionId,2003        item_id: TokenId,2004        mode: &CollectionMode2005    ) -> DispatchResult {2006        match mode {2007            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2008            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2009            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2010            _ => ()2011        };2012        2013        Ok(())2014    }20152016    fn set_re_fungible_variable_data(2017        collection_id: CollectionId,2018        item_id: TokenId,2019        data: Vec<u8>2020    ) -> DispatchResult {2021        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20222023        item.variable_data = data;20242025        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20262027        Ok(())2028    }20292030    fn set_nft_variable_data(2031        collection_id: CollectionId,2032        item_id: TokenId,2033        data: Vec<u8>2034    ) -> DispatchResult {2035        let mut item = <NftItemList<T>>::get(collection_id, item_id);2036        2037        item.variable_data = data;20382039        <NftItemList<T>>::insert(collection_id, item_id, item);2040        2041        Ok(())2042    }20432044    fn init_collection(item: &CollectionType<T::AccountId>) {2045        // check params2046        assert!(2047            item.decimal_points <= MAX_DECIMAL_POINTS,2048            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2049        );2050        assert!(2051            item.name.len() <= 64,2052            "Collection name can not be longer than 63 char"2053        );2054        assert!(2055            item.name.len() <= 256,2056            "Collection description can not be longer than 255 char"2057        );2058        assert!(2059            item.token_prefix.len() <= 16,2060            "Token prefix can not be longer than 15 char"2061        );20622063        // Generate next collection ID2064        let next_id = CreatedCollectionCount::get()2065            .checked_add(1)2066            .unwrap();20672068        CreatedCollectionCount::put(next_id);2069    }20702071    fn init_nft_token(item: &NftItemType<T::AccountId>) {2072        let current_index = <ItemListIndex>::get(item.collection)2073            .checked_add(1)2074            .unwrap();20752076        let item_owner = item.owner.clone();2077        let collection_id = item.collection.clone();2078        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();20792080        <ItemListIndex>::insert(collection_id, current_index);20812082        // Update balance2083        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2084            .checked_add(1)2085            .unwrap();2086        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2087    }20882089    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2090        let current_index = <ItemListIndex>::get(item.collection)2091            .checked_add(1)2092            .unwrap();2093        let owner = item.owner.clone();20942095        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20962097        <ItemListIndex>::insert(item.collection, current_index);20982099        // Update balance2100        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2101            .checked_add(item.value)2102            .unwrap();2103        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2104    }21052106    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2107        let current_index = <ItemListIndex>::get(item.collection)2108            .checked_add(1)2109            .unwrap();21102111        let value = item.owner.first().unwrap().fraction;2112        let owner = item.owner.first().unwrap().owner.clone();21132114        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();21152116        <ItemListIndex>::insert(item.collection, current_index);21172118        // Update balance2119        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2120            .checked_add(value)2121            .unwrap();2122        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2123    }21242125    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {21262127        // add to account limit2128        if <AccountItemCount<T>>::contains_key(owner.clone()) {21292130            // bound Owned tokens by a single address2131            let count = <AccountItemCount<T>>::get(owner.clone());2132            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21332134            <AccountItemCount<T>>::insert(owner.clone(), count2135                .checked_add(1)2136                .ok_or(Error::<T>::NumOverflow)?);2137        }2138        else {2139            <AccountItemCount<T>>::insert(owner.clone(), 1);2140        }21412142        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2143        if list_exists {2144            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2145            let item_contains = list.contains(&item_index.clone());21462147            if !item_contains {2148                list.push(item_index.clone());2149            }21502151            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2152        } else {2153            let mut itm = Vec::new();2154            itm.push(item_index.clone());2155            <AddressTokens<T>>::insert(collection_id, owner, itm);2156            2157        }21582159        Ok(())2160    }21612162    fn remove_token_index(2163        collection_id: CollectionId,2164        item_index: TokenId,2165        owner: T::AccountId,2166    ) -> DispatchResult {21672168        // update counter2169        <AccountItemCount<T>>::insert(owner.clone(), 2170            <AccountItemCount<T>>::get(owner.clone())2171            .checked_sub(1)2172            .ok_or(Error::<T>::NumOverflow)?);217321742175        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2176        if list_exists {2177            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2178            let item_contains = list.contains(&item_index.clone());21792180            if item_contains {2181                list.retain(|&item| item != item_index);2182                <AddressTokens<T>>::insert(collection_id, owner, list);2183            }2184        }21852186        Ok(())2187    }21882189    fn move_token_index(2190        collection_id: CollectionId,2191        item_index: TokenId,2192        old_owner: T::AccountId,2193        new_owner: T::AccountId,2194    ) -> DispatchResult {2195        Self::remove_token_index(collection_id, item_index, old_owner)?;2196        Self::add_token_index(collection_id, item_index, new_owner)?;21972198        Ok(())2199    }2200}22012202////////////////////////////////////////////////////////////////////////////////////////////////////2203// Economic models2204// #region22052206/// Fee multiplier.2207pub type Multiplier = FixedU128;22082209type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2210    <T as system::Trait>::AccountId,2211>>::Balance;2212type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2213    <T as system::Trait>::AccountId,2214>>::NegativeImbalance;22152216/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2217/// in the queue.2218#[derive(Encode, Decode, Clone, Eq, PartialEq)]2219pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2220    #[codec(compact)] BalanceOf<T>2221);22222223impl<T: Trait + Send + Sync> sp_std::fmt::Debug2224    for ChargeTransactionPayment<T>2225{2226    #[cfg(feature = "std")]2227    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2228        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2229    }2230    #[cfg(not(feature = "std"))]2231    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2232        Ok(())2233    }2234}22352236impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2237where2238    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2239    BalanceOf<T>: Send + Sync + FixedPointOperand,2240{2241    /// utility constructor. Used only in client/factory code.2242    pub fn from(fee: BalanceOf<T>) -> Self {2243        Self(fee)2244    }22452246    pub fn traditional_fee(2247        len: usize,2248        info: &DispatchInfoOf<T::Call>,2249        tip: BalanceOf<T>,2250    ) -> BalanceOf<T>2251    where2252        T::Call: Dispatchable<Info = DispatchInfo>,2253    {2254        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2255    }22562257    fn withdraw_fee(2258        &self,2259        who: &T::AccountId,2260        call: &T::Call,2261        info: &DispatchInfoOf<T::Call>,2262        len: usize,2263    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2264        let tip = self.0;22652266        // Set fee based on call type. Creating collection costs 1 Unique.2267        // All other transactions have traditional fees so far2268        // let fee = match call.is_sub_type() {2269        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2270        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2271        //                                                 // _ => <BalanceOf<T>>::from(100)2272        // };2273        let fee = Self::traditional_fee(len, info, tip);22742275        // Determine who is paying transaction fee based on ecnomic model2276        // Parse call to extract collection ID and access collection sponsor2277        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2278            Some(Call::create_item(collection_id, _owner, _properties)) => {22792280                // check free create limit2281                if <Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)2282                {2283                    <Collection<T>>::get(collection_id).sponsor2284                } else {2285                    T::AccountId::default()2286                }2287            }2288            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2289                2290                let _collection_limits = <Collection<T>>::get(collection_id).limits;2291                let _collection_mode = <Collection<T>>::get(collection_id).mode;22922293                // sponsor timeout2294                let sponsor_transfer = match _collection_mode {2295                    CollectionMode::NFT => {22962297                        // get correct limit2298                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2299                            _collection_limits.sponsor_transfer_timeout2300                        } else {2301                            ChainLimit::get().nft_sponsor_transfer_timeout2302                        };23032304                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2305                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2306                        let limit_time = basket + limit.into();2307                        if block_number >= limit_time {2308                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2309                            true2310                        }2311                        else {2312                            false2313                        }2314                    }2315                    CollectionMode::Fungible(_) => {23162317                        // get correct limit2318                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2319                            _collection_limits.sponsor_transfer_timeout2320                        } else {2321                            ChainLimit::get().fungible_sponsor_transfer_timeout2322                        };23232324                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2325                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2326                        if basket.iter().any(|i| i.address == _new_owner.clone())2327                        {2328                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2329                            let limit_time = item.start_block + limit.into();2330                            if block_number >= limit_time {2331                                basket.retain(|x| x.address == item.address);2332                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2333                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2334                                true2335                            }2336                            else {2337                                false2338                            }2339                        }2340                        else {2341                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2342                            true2343                        }2344                    }2345                    CollectionMode::ReFungible(_) => {23462347                        // get correct limit2348                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2349                            _collection_limits.sponsor_transfer_timeout2350                        } else {2351                            ChainLimit::get().refungible_sponsor_transfer_timeout2352                        };23532354                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2355                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2356                        let limit_time = basket + limit.into();2357                        if block_number >= limit_time {2358                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2359                            true2360                        } else {2361                            false2362                        }2363                    }2364                    _ => {2365                        false2366                    },2367                };23682369                if !sponsor_transfer {2370                    T::AccountId::default()2371                } else {2372                    <Collection<T>>::get(collection_id).sponsor2373                }2374            }23752376            _ => T::AccountId::default(),2377        };23782379        // Sponsor smart contracts2380        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {23812382            // On instantiation: set the contract owner2383            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {23842385                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2386                    code_hash,2387                    &data,2388                    &who,2389                );2390                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());23912392                T::AccountId::default()2393            },23942395            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2396            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {23972398                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());23992400                let mut sponsor_transfer = false;2401                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2402                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2403                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2404                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2405                    let limit_time = last_tx_block + rate_limit;24062407                    if block_number >= limit_time {2408                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2409                        sponsor_transfer = true;2410                    }2411                } else {2412                    sponsor_transfer = false;2413                }2414               2415                2416                let mut sp = T::AccountId::default();2417                if sponsor_transfer {2418                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2419                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2420                            sp = called_contract;2421                        }2422                    }2423                }24242425                sp2426            },24272428            _ => sponsor,2429        };24302431        let mut who_pays_fee: T::AccountId = sponsor.clone();2432        if sponsor == T::AccountId::default() {2433            who_pays_fee = who.clone();2434        }24352436        // Only mess with balances if fee is not zero.2437        if fee.is_zero() {2438            return Ok((fee, None));2439        }24402441        match <T as transaction_payment::Trait>::Currency::withdraw(2442            &who_pays_fee,2443            fee,2444            if tip.is_zero() {2445                WithdrawReason::TransactionPayment.into()2446            } else {2447                WithdrawReason::TransactionPayment | WithdrawReason::Tip2448            },2449            ExistenceRequirement::KeepAlive,2450        ) {2451            Ok(imbalance) => Ok((fee, Some(imbalance))),2452            Err(_) => Err(InvalidTransaction::Payment.into()),2453        }2454    }2455}245624572458impl<T: Trait + Send + Sync> SignedExtension2459    for ChargeTransactionPayment<T>2460where2461    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2462    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2463{2464    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2465    type AccountId = T::AccountId;2466    type Call = T::Call;2467    type AdditionalSigned = ();2468    type Pre = (2469        BalanceOf<T>,2470        Self::AccountId,2471        Option<NegativeImbalanceOf<T>>,2472        BalanceOf<T>,2473    );2474    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2475        Ok(())2476    }24772478    fn validate(2479        &self,2480        _who: &Self::AccountId,2481        _call: &Self::Call,2482        _info: &DispatchInfoOf<Self::Call>,2483        _len: usize,2484    ) -> TransactionValidity {2485        Ok(ValidTransaction::default())2486    }24872488    fn pre_dispatch(2489        self,2490        who: &Self::AccountId,2491        call: &Self::Call,2492        info: &DispatchInfoOf<Self::Call>,2493        len: usize,2494    ) -> Result<Self::Pre, TransactionValidityError> {2495        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2496        Ok((self.0, who.clone(), imbalance, fee))2497    }24982499    fn post_dispatch(2500        pre: Self::Pre,2501        info: &DispatchInfoOf<Self::Call>,2502        post_info: &PostDispatchInfoOf<Self::Call>,2503        len: usize,2504        _result: &DispatchResult,2505    ) -> Result<(), TransactionValidityError> {2506        let (tip, who, imbalance, fee) = pre;2507        if let Some(payed) = imbalance {2508            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2509                len as u32, info, post_info, tip,2510            );2511            let refund = fee.saturating_sub(actual_fee);2512            let actual_payment =2513                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2514                    &who, refund,2515                ) {2516                    Ok(refund_imbalance) => {2517                        // The refund cannot be larger than the up front payed max weight.2518                        // `PostDispatchInfo::calc_unspent` guards against such a case.2519                        match payed.offset(refund_imbalance) {2520                            Ok(actual_payment) => actual_payment,2521                            Err(_) => return Err(InvalidTransaction::Payment.into()),2522                        }2523                    }2524                    // We do not recreate the account using the refund. The up front payment2525                    // is gone in that case.2526                    Err(_) => payed,2527                };2528            let imbalances = actual_payment.split(tip);2529            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2530                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2531            );2532        }2533        Ok(())2534    }2535}25362537// #endregion
after · pallets/nft/src/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]23#[cfg(feature = "std")]4pub use std::*;56#[cfg(feature = "std")]7pub use serde::*;89use codec::{Decode, Encode};10pub use frame_support::{11    construct_runtime, decl_event, decl_module, decl_storage, decl_error,12    dispatch::DispatchResult,13    ensure, fail, parameter_types,14    traits::{15        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,16        Randomness, WithdrawReason,17    },18    weights::{19        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},20        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,21        WeightToFeePolynomial,22    },23    IsSubType, StorageValue,24};2526use frame_system::{self as system, ensure_signed, ensure_root};27use sp_runtime::sp_std::prelude::Vec;28use sp_runtime::{29    traits::{30        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SignedExtension, Zero,31    },32    transaction_validity::{33        InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,34    },35    FixedPointOperand, FixedU128,36};37use pallet_contracts::ContractAddressFor;38use sp_runtime::traits::StaticLookup;3940#[cfg(test)]41mod mock;4243#[cfg(test)]44mod tests;4546mod default_weights;4748pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;49pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;50pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;5152// Structs53// #region5455pub type CollectionId = u32;56pub type TokenId = u32;5758pub type DecimalPoints = u8;5960#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]61#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]62pub enum CollectionMode {63    Invalid,64    NFT,65    // decimal points66    Fungible(DecimalPoints),67    // decimal points68    ReFungible(DecimalPoints),69}7071impl Into<u8> for CollectionMode {72    fn into(self) -> u8 {73        match self {74            CollectionMode::Invalid => 0,75            CollectionMode::NFT => 1,76            CollectionMode::Fungible(_) => 2,77            CollectionMode::ReFungible(_) => 3,78        }79    }80}8182#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]83#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]84pub enum AccessMode {85    Normal,86    WhiteList,87}88impl Default for AccessMode {89    fn default() -> Self {90        Self::Normal91    }92}9394impl Default for CollectionMode {95    fn default() -> Self {96        Self::Invalid97    }98}99100#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]101#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]102pub struct Ownership<AccountId> {103    pub owner: AccountId,104    pub fraction: u128,105}106107#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]108#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]109pub struct CollectionType<AccountId> {110    pub owner: AccountId,111    pub mode: CollectionMode,112    pub access: AccessMode,113    pub decimal_points: DecimalPoints,114    pub name: Vec<u16>,        // 64 include null escape char115    pub description: Vec<u16>, // 256 include null escape char116    pub token_prefix: Vec<u8>, // 16 include null escape char117    pub mint_mode: bool,118    pub offchain_schema: Vec<u8>,119    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender120    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship121    pub limits: CollectionLimits, // Collection private restrictions 122    pub variable_on_chain_schema: Vec<u8>, //123    pub const_on_chain_schema: Vec<u8>, //124}125126#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub struct NftItemType<AccountId> {129    pub collection: CollectionId,130    pub owner: AccountId,131    pub const_data: Vec<u8>,132    pub variable_data: Vec<u8>,133}134135#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]136#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]137pub struct FungibleItemType<AccountId> {138    pub collection: CollectionId,139    pub owner: AccountId,140    pub value: u128,141}142143#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]144#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]145pub struct ReFungibleItemType<AccountId> {146    pub collection: CollectionId,147    pub owner: Vec<Ownership<AccountId>>,148    pub const_data: Vec<u8>,149    pub variable_data: Vec<u8>,150}151152#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]153#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]154pub struct ApprovePermissions<AccountId> {155    pub approved: AccountId,156    pub amount: u128,157}158159#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]160#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]161pub struct VestingItem<AccountId, Moment> {162    pub sender: AccountId,163    pub recipient: AccountId,164    pub collection_id: CollectionId,165    pub item_id: TokenId,166    pub amount: u64,167    pub vesting_date: Moment,168}169170#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]171#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]172pub struct BasketItem<AccountId, BlockNumber> {173    pub address: AccountId,174    pub start_block: BlockNumber,175}176177#[derive(Encode, Decode, Debug, Clone, PartialEq)]178#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]179pub struct CollectionLimits {180    pub account_token_ownership_limit: u32,181    pub sponsored_data_size: u32,182    pub token_limit: u32,183184    // Timeouts for item types in passed blocks185    pub sponsor_transfer_timeout: u32,186}187188impl Default for CollectionLimits {189    fn default() -> CollectionLimits {190        CollectionLimits { 191            account_token_ownership_limit: 10_000_000, 192            token_limit: u32::max_value(),193            sponsored_data_size: u32::max_value(), 194            sponsor_transfer_timeout: 14400 }195    }196}197198#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]199#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]200pub struct ChainLimits {201    pub collection_numbers_limit: u32,202    pub account_token_ownership_limit: u32,203    pub collections_admins_limit: u64,204    pub custom_data_limit: u32,205206    // Timeouts for item types in passed blocks207    pub nft_sponsor_transfer_timeout: u32,208    pub fungible_sponsor_transfer_timeout: u32,209    pub refungible_sponsor_transfer_timeout: u32,210}211212pub trait WeightInfo {213	fn create_collection() -> Weight;214	fn destroy_collection() -> Weight;215	fn add_to_white_list() -> Weight;216	fn remove_from_white_list() -> Weight;217    fn set_public_access_mode() -> Weight;218    fn set_mint_permission() -> Weight;219    fn change_collection_owner() -> Weight;220    fn add_collection_admin() -> Weight;221    fn remove_collection_admin() -> Weight;222    fn set_collection_sponsor() -> Weight;223    fn confirm_sponsorship() -> Weight;224    fn remove_collection_sponsor() -> Weight;225    fn create_item(s: usize) -> Weight;226    fn burn_item() -> Weight;227    fn transfer() -> Weight;228    fn approve() -> Weight;229    fn transfer_from() -> Weight;230    fn set_offchain_schema() -> Weight;231    fn set_const_on_chain_schema() -> Weight;232    fn set_variable_on_chain_schema() -> Weight;233    fn set_variable_meta_data() -> Weight;234    fn enable_contract_sponsoring() -> Weight;235}236237#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]238#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]239pub struct CreateNftData {240    pub const_data: Vec<u8>,241    pub variable_data: Vec<u8>,242}243244#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]245#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]246pub struct CreateFungibleData {247}248249#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]250#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]251pub struct CreateReFungibleData {252    pub const_data: Vec<u8>,253    pub variable_data: Vec<u8>,254}255256#[derive(Encode, Decode, Debug, Clone, PartialEq)]257#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]258pub enum CreateItemData {259    NFT(CreateNftData),260    Fungible(CreateFungibleData),261    ReFungible(CreateReFungibleData)262}263264impl CreateItemData {265    pub fn len(&self) -> usize {266        let len = match self {267            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),268            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),269            _ => 0270        };271        272        return len;273    }274}275276impl From<CreateNftData> for CreateItemData {277    fn from(item: CreateNftData) -> Self {278        CreateItemData::NFT(item)279    }280}281282impl From<CreateReFungibleData> for CreateItemData {283    fn from(item: CreateReFungibleData) -> Self {284        CreateItemData::ReFungible(item)285    }286}287288impl From<CreateFungibleData> for CreateItemData {289    fn from(item: CreateFungibleData) -> Self {290        CreateItemData::Fungible(item)291    }292}293294295decl_error! {296	/// Error for non-fungible-token module.297	pub enum Error for Module<T: Trait> {298        /// Total collections bound exceeded.299        TotalCollectionsLimitExceeded,300		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.301        CollectionDecimalPointLimitExceeded, 302        /// Collection name can not be longer than 63 char.303        CollectionNameLimitExceeded, 304        /// Collection description can not be longer than 255 char.305        CollectionDescriptionLimitExceeded, 306        /// Token prefix can not be longer than 15 char.307        CollectionTokenPrefixLimitExceeded,308        /// This collection does not exist.309        CollectionNotFound,310        /// Item not exists.311        TokenNotFound,312        /// Arithmetic calculation overflow.313        NumOverflow,       314        /// Account already has admin role.315        AlreadyAdmin,  316        /// You do not own this collection.317        NoPermission,318        /// This address is not set as sponsor, use setCollectionSponsor first.319        ConfirmUnsetSponsorFail,320        /// Collection is not in mint mode.321        PublicMintingNotAllowed,322        /// Sender parameter and item owner must be equal.323        MustBeTokenOwner,324        /// Item balance not enough.325        TokenValueTooLow,326        /// Size of item is too large.327        NftSizeLimitExceeded,328        /// No approve found329        ApproveNotFound,330        /// Requested value more than approved.331        TokenValueNotEnough,332        /// Only approved addresses can call this method.333        ApproveRequired,334        /// Address is not in white list.335        AddresNotInWhiteList,336        /// Number of collection admins bound exceeded.337        CollectionAdminsLimitExceeded,338        /// Owned tokens by a single address bound exceeded.339        AddressOwnershipLimitExceeded,340        /// Length of items properties must be greater than 0.341        EmptyArgument,342        /// const_data exceeded data limit.343        TokenConstDataLimitExceeded,344        /// variable_data exceeded data limit.345        TokenVariableDataLimitExceeded,346        /// Not NFT item data used to mint in NFT collection.347        NotNftDataUsedToMintNftCollectionToken,348        /// Not Fungible item data used to mint in Fungible collection.349        NotFungibleDataUsedToMintFungibleCollectionToken,350        /// Not Re Fungible item data used to mint in Re Fungible collection.351        NotReFungibleDataUsedToMintReFungibleCollectionToken,352        /// Unexpected collection type.353        UnexpectedCollectionType,354        /// Can't store metadata in fungible tokens.355        CantStoreMetadataInFungibleTokens,356        /// Collection token limit exceeded357        CollectionTokenLimitExceeded,358        /// Account token limit exceeded per collection359        AccountTokenLimitExceeded,360        /// Collection limit bounds per collection exceeded361        CollectionLimitBoundsExceeded362	}363}364365pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {366    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;367368    /// Weight information for extrinsics in this pallet.369	type WeightInfo: WeightInfo;370}371372#[cfg(feature = "runtime-benchmarks")]373mod benchmarking;374375// #endregion376377decl_storage! {378    trait Store for Module<T: Trait> as Nft {379380        // Private members381        NextCollectionID: CollectionId;382        CreatedCollectionCount: u32;383        ChainVersion: u64;384        ItemListIndex: map hasher(identity) CollectionId => TokenId;385386        // Chain limits struct387        pub ChainLimit get(fn chain_limit) config(): ChainLimits;388389        // Bound counters390        CollectionCount: u32;391        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;392393        // Basic collections394        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;395        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;396        pub WhiteList get(fn white_list): map hasher(identity) CollectionId => Vec<T::AccountId>;397398        /// Balance owner per collection map399        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;400401        /// second parameter: item id + owner account id402        pub ApprovedList get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;403404        /// Item collections405        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;406        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => FungibleItemType<T::AccountId>;407        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;408409        /// Index list410        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;411412        /// Tokens transfer baskets413        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;414        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Vec<BasketItem<T::AccountId, T::BlockNumber>>;415        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;416417        // Contract Sponsorship and Ownership418        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;419        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;420        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;421        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;422    }423    add_extra_genesis {424        build(|config: &GenesisConfig<T>| {425            // Modification of storage426            for (_num, _c) in &config.collection {427                <Module<T>>::init_collection(_c);428            }429430            for (_num, _q, _i) in &config.nft_item_id {431                <Module<T>>::init_nft_token(_i);432            }433434            for (_num, _q, _i) in &config.fungible_item_id {435                <Module<T>>::init_fungible_token(_i);436            }437438            for (_num, _q, _i) in &config.refungible_item_id {439                <Module<T>>::init_refungible_token(_i);440            }441        })442    }443}444445decl_event!(446    pub enum Event<T>447    where448        AccountId = <T as system::Trait>::AccountId,449    {450        /// New collection was created451        /// 452        /// # Arguments453        /// 454        /// * collection_id: Globally unique identifier of newly created collection.455        /// 456        /// * mode: [CollectionMode] converted into u8.457        /// 458        /// * account_id: Collection owner.459        Created(CollectionId, u8, AccountId),460461        /// New item was created.462        /// 463        /// # Arguments464        /// 465        /// * collection_id: Id of the collection where item was created.466        /// 467        /// * item_id: Id of an item. Unique within the collection.468        ItemCreated(CollectionId, TokenId),469470        /// Collection item was burned.471        /// 472        /// # Arguments473        /// 474        /// collection_id.475        /// 476        /// item_id: Identifier of burned NFT.477        ItemDestroyed(CollectionId, TokenId),478    }479);480481decl_module! {482    pub struct Module<T: Trait> for enum Call where origin: T::Origin {483484        fn deposit_event() = default;485        type Error = Error<T>;486487        fn on_initialize(now: T::BlockNumber) -> Weight {488489            if ChainVersion::get() < 2490            {491                let value = NextCollectionID::get();492                CreatedCollectionCount::put(value);493                ChainVersion::put(2);494            }495496            0497        }498499        /// 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.500        /// 501        /// # Permissions502        /// 503        /// * Anyone.504        /// 505        /// # Arguments506        /// 507        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.508        /// 509        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.510        /// 511        /// * token_prefix: UTF-8 string with token prefix.512        /// 513        /// * mode: [CollectionMode] collection type and type dependent data.514        // returns collection ID515        #[weight = T::WeightInfo::create_collection()]516        pub fn create_collection(origin,517                                 collection_name: Vec<u16>,518                                 collection_description: Vec<u16>,519                                 token_prefix: Vec<u8>,520                                 mode: CollectionMode) -> DispatchResult {521522            // Anyone can create a collection523            let who = ensure_signed(origin)?;524525            let decimal_points = match mode {526                CollectionMode::Fungible(points) => points,527                CollectionMode::ReFungible(points) => points,528                _ => 0529            };530531            // bound Total number of collections532            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);533534            // check params535            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);536537            let mut name = collection_name.to_vec();538            name.push(0);539            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);540541            let mut description = collection_description.to_vec();542            description.push(0);543            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);544545            let mut prefix = token_prefix.to_vec();546            prefix.push(0);547            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);548549            // Generate next collection ID550            let next_id = CreatedCollectionCount::get()551                .checked_add(1)552                .ok_or(Error::<T>::NumOverflow)?;553554            // bound counter555            let total = CollectionCount::get()556                .checked_add(1)557                .ok_or(Error::<T>::NumOverflow)?;558559            CreatedCollectionCount::put(next_id);560            CollectionCount::put(total);561562            // Create new collection563            let new_collection = CollectionType {564                owner: who.clone(),565                name: name,566                mode: mode.clone(),567                mint_mode: false,568                access: AccessMode::Normal,569                description: description,570                decimal_points: decimal_points,571                token_prefix: prefix,572                offchain_schema: Vec::new(),573                sponsor: T::AccountId::default(),574                unconfirmed_sponsor: T::AccountId::default(),575                variable_on_chain_schema: Vec::new(),576                const_on_chain_schema: Vec::new(),577                limits: CollectionLimits::default(),578            };579580            // Add new collection to map581            <Collection<T>>::insert(next_id, new_collection);582583            // call event584            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));585586            Ok(())587        }588589        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.590        /// 591        /// # Permissions592        /// 593        /// * Collection Owner.594        /// 595        /// # Arguments596        /// 597        /// * collection_id: collection to destroy.598        #[weight = T::WeightInfo::destroy_collection()]599        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {600601            let sender = ensure_signed(origin)?;602            Self::check_owner_permissions(collection_id, sender)?;603604            <AddressTokens<T>>::remove_prefix(collection_id);605            <ApprovedList<T>>::remove_prefix(collection_id);606            <Balance<T>>::remove_prefix(collection_id);607            <ItemListIndex>::remove(collection_id);608            <AdminList<T>>::remove(collection_id);609            <Collection<T>>::remove(collection_id);610            <WhiteList<T>>::remove(collection_id);611612            <NftItemList<T>>::remove_prefix(collection_id);613            <FungibleItemList<T>>::remove_prefix(collection_id);614            <ReFungibleItemList<T>>::remove_prefix(collection_id);615616            <NftTransferBasket<T>>::remove_prefix(collection_id);617            <FungibleTransferBasket<T>>::remove_prefix(collection_id);618            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);619620            if CollectionCount::get() > 0621            {622                // bound couter623                let total = CollectionCount::get()624                    .checked_sub(1)625                    .ok_or(Error::<T>::NumOverflow)?;626627                CollectionCount::put(total);628            }629630            Ok(())631        }632633        /// Add an address to white list.634        /// 635        /// # Permissions636        /// 637        /// * Collection Owner638        /// * Collection Admin639        /// 640        /// # Arguments641        /// 642        /// * collection_id.643        /// 644        /// * address.645        #[weight = T::WeightInfo::add_to_white_list()]646        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{647648            let sender = ensure_signed(origin)?;649            Self::check_owner_or_admin_permissions(collection_id, sender)?;650651            let mut white_list_collection: Vec<T::AccountId>;652            if <WhiteList<T>>::contains_key(collection_id) {653                white_list_collection = <WhiteList<T>>::get(collection_id);654                if !white_list_collection.contains(&address.clone())655                {656                    white_list_collection.push(address.clone());657                }658            }659            else {660                white_list_collection = Vec::new();661                white_list_collection.push(address.clone());662            }663664            <WhiteList<T>>::insert(collection_id, white_list_collection);665            Ok(())666        }667668        /// Remove an address from white list.669        /// 670        /// # Permissions671        /// 672        /// * Collection Owner673        /// * Collection Admin674        /// 675        /// # Arguments676        /// 677        /// * collection_id.678        /// 679        /// * address.680        #[weight = T::WeightInfo::remove_from_white_list()]681        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{682683            let sender = ensure_signed(origin)?;684            Self::check_owner_or_admin_permissions(collection_id, sender)?;685686            if <WhiteList<T>>::contains_key(collection_id) {687                let mut white_list_collection = <WhiteList<T>>::get(collection_id);688                if white_list_collection.contains(&address.clone())689                {690                    white_list_collection.retain(|i| *i != address.clone());691                    <WhiteList<T>>::insert(collection_id, white_list_collection);692                }693            }694695            Ok(())696        }697698        /// Toggle between normal and white list access for the methods with access for `Anyone`.699        /// 700        /// # Permissions701        /// 702        /// * Collection Owner.703        /// 704        /// # Arguments705        /// 706        /// * collection_id.707        /// 708        /// * mode: [AccessMode]709        #[weight = T::WeightInfo::set_public_access_mode()]710        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult711        {712            let sender = ensure_signed(origin)?;713714            Self::check_owner_permissions(collection_id, sender)?;715            let mut target_collection = <Collection<T>>::get(collection_id);716            target_collection.access = mode;717            <Collection<T>>::insert(collection_id, target_collection);718719            Ok(())720        }721722        /// Allows Anyone to create tokens if:723        /// * White List is enabled, and724        /// * Address is added to white list, and725        /// * This method was called with True parameter726        /// 727        /// # Permissions728        /// * Collection Owner729        ///730        /// # Arguments731        /// 732        /// * collection_id.733        /// 734        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.735        #[weight = T::WeightInfo::set_mint_permission()]736        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult737        {738            let sender = ensure_signed(origin)?;739740            Self::check_owner_permissions(collection_id, sender)?;741            let mut target_collection = <Collection<T>>::get(collection_id);742            target_collection.mint_mode = mint_permission;743            <Collection<T>>::insert(collection_id, target_collection);744745            Ok(())746        }747748        /// Change the owner of the collection.749        /// 750        /// # Permissions751        /// 752        /// * Collection Owner.753        /// 754        /// # Arguments755        /// 756        /// * collection_id.757        /// 758        /// * new_owner.759        #[weight = T::WeightInfo::change_collection_owner()]760        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {761762            let sender = ensure_signed(origin)?;763            Self::check_owner_permissions(collection_id, sender)?;764            let mut target_collection = <Collection<T>>::get(collection_id);765            target_collection.owner = new_owner;766            <Collection<T>>::insert(collection_id, target_collection);767768            Ok(())769        }770771        /// Adds an admin of the Collection.772        /// 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. 773        /// 774        /// # Permissions775        /// 776        /// * Collection Owner.777        /// * Collection Admin.778        /// 779        /// # Arguments780        /// 781        /// * collection_id: ID of the Collection to add admin for.782        /// 783        /// * new_admin_id: Address of new admin to add.784        #[weight = T::WeightInfo::add_collection_admin()]785        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {786787            let sender = ensure_signed(origin)?;788            Self::check_owner_or_admin_permissions(collection_id, sender)?;789            let mut admin_arr: Vec<T::AccountId> = Vec::new();790791            if <AdminList<T>>::contains_key(collection_id)792            {793                admin_arr = <AdminList<T>>::get(collection_id);794                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);795            }796797            // Number of collection admins798            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);799800            admin_arr.push(new_admin_id);801            <AdminList<T>>::insert(collection_id, admin_arr);802803            Ok(())804        }805806        /// 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.807        ///808        /// # Permissions809        /// 810        /// * Collection Owner.811        /// * Collection Admin.812        /// 813        /// # Arguments814        /// 815        /// * collection_id: ID of the Collection to remove admin for.816        /// 817        /// * account_id: Address of admin to remove.818        #[weight = T::WeightInfo::remove_collection_admin()]819        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {820821            let sender = ensure_signed(origin)?;822            Self::check_owner_or_admin_permissions(collection_id, sender)?;823824            if <AdminList<T>>::contains_key(collection_id)825            {826                let mut admin_arr = <AdminList<T>>::get(collection_id);827                admin_arr.retain(|i| *i != account_id);828                <AdminList<T>>::insert(collection_id, admin_arr);829            }830831            Ok(())832        }833834        /// # Permissions835        /// 836        /// * Collection Owner837        /// 838        /// # Arguments839        /// 840        /// * collection_id.841        /// 842        /// * new_sponsor.843        #[weight = T::WeightInfo::set_collection_sponsor()]844        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {845846            let sender = ensure_signed(origin)?;847            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);848849            let mut target_collection = <Collection<T>>::get(collection_id);850            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);851852            target_collection.unconfirmed_sponsor = new_sponsor;853            <Collection<T>>::insert(collection_id, target_collection);854855            Ok(())856        }857858        /// # Permissions859        /// 860        /// * Sponsor.861        /// 862        /// # Arguments863        /// 864        /// * collection_id.865        #[weight = T::WeightInfo::confirm_sponsorship()]866        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {867868            let sender = ensure_signed(origin)?;869            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);870871            let mut target_collection = <Collection<T>>::get(collection_id);872            ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);873874            target_collection.sponsor = target_collection.unconfirmed_sponsor;875            target_collection.unconfirmed_sponsor = T::AccountId::default();876            <Collection<T>>::insert(collection_id, target_collection);877878            Ok(())879        }880881        /// Switch back to pay-per-own-transaction model.882        ///883        /// # Permissions884        ///885        /// * Collection owner.886        /// 887        /// # Arguments888        /// 889        /// * collection_id.890        #[weight = T::WeightInfo::remove_collection_sponsor()]891        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {892893            let sender = ensure_signed(origin)?;894            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);895896            let mut target_collection = <Collection<T>>::get(collection_id);897            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);898899            target_collection.sponsor = T::AccountId::default();900            <Collection<T>>::insert(collection_id, target_collection);901902            Ok(())903        }904905        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.906        /// 907        /// # Permissions908        /// 909        /// * Collection Owner.910        /// * Collection Admin.911        /// * Anyone if912        ///     * White List is enabled, and913        ///     * Address is added to white list, and914        ///     * MintPermission is enabled (see SetMintPermission method)915        /// 916        /// # Arguments917        /// 918        /// * collection_id: ID of the collection.919        /// 920        /// * owner: Address, initial owner of the NFT.921        ///922        /// * data: Token data to store on chain.923        // #[weight =924        // (130_000_000 as Weight)925        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))926        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))927        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]928929        #[weight = T::WeightInfo::create_item(data.len())]930        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {931932            let sender = ensure_signed(origin)?;933934            Self::collection_exists(collection_id)?;935936            let target_collection = <Collection<T>>::get(collection_id);937938            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;939            Self::validate_create_item_args(&target_collection, &data)?;940            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;941942            Ok(())943        }944945        /// This method creates multiple instances of NFT Collection created with CreateCollection method.946        /// 947        /// # Permissions948        /// 949        /// * Collection Owner.950        /// * Collection Admin.951        /// * Anyone if952        ///     * White List is enabled, and953        ///     * Address is added to white list, and954        ///     * MintPermission is enabled (see SetMintPermission method)955        /// 956        /// # Arguments957        /// 958        /// * collection_id: ID of the collection.959        /// 960        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].961        /// 962        /// * owner: Address, initial owner of the NFT.963        #[weight = T::WeightInfo::create_item(items_data.into_iter()964                               .map(|data| { data.len() })965                               .sum())]966        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {967968            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);969            let sender = ensure_signed(origin)?;970971            Self::collection_exists(collection_id)?;972            let target_collection = <Collection<T>>::get(collection_id);973974            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;975976            for data in &items_data {977                Self::validate_create_item_args(&target_collection, data)?;978            }979            for data in &items_data {980                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;981            }982983            Ok(())984        }985986        /// Destroys a concrete instance of NFT.987        /// 988        /// # Permissions989        /// 990        /// * Collection Owner.991        /// * Collection Admin.992        /// * Current NFT Owner.993        /// 994        /// # Arguments995        /// 996        /// * collection_id: ID of the collection.997        /// 998        /// * item_id: ID of NFT to burn.999        #[weight = T::WeightInfo::burn_item()]1000        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {10011002            let sender = ensure_signed(origin)?;1003            Self::collection_exists(collection_id)?;10041005            // Transfer permissions check1006            let target_collection = <Collection<T>>::get(collection_id);1007            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1008                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1009                Error::<T>::NoPermission);10101011            if target_collection.access == AccessMode::WhiteList {1012                Self::check_white_list(collection_id, &sender)?;1013            }10141015            match target_collection.mode1016            {1017                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1018                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,1019                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1020                _ => ()1021            };10221023            // call event1024            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10251026            Ok(())1027        }10281029        /// Change ownership of the token.1030        /// 1031        /// # Permissions1032        /// 1033        /// * Collection Owner1034        /// * Collection Admin1035        /// * Current NFT owner1036        ///1037        /// # Arguments1038        /// 1039        /// * recipient: Address of token recipient.1040        /// 1041        /// * collection_id.1042        /// 1043        /// * item_id: ID of the item1044        ///     * Non-Fungible Mode: Required.1045        ///     * Fungible Mode: Ignored.1046        ///     * Re-Fungible Mode: Required.1047        /// 1048        /// * value: Amount to transfer.1049        ///     * Non-Fungible Mode: Ignored1050        ///     * Fungible Mode: Must specify transferred amount1051        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1052        #[weight = T::WeightInfo::transfer()]1053        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10541055            let sender = ensure_signed(origin)?;1056            let target_collection = <Collection<T>>::get(collection_id);10571058            // Limits check1059            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;10601061            // Transfer permissions check1062            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1063                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1064                Error::<T>::NoPermission);10651066            if target_collection.access == AccessMode::WhiteList {1067                Self::check_white_list(collection_id, &sender)?;1068                Self::check_white_list(collection_id, &recipient)?;1069            }10701071            match target_collection.mode1072            {1073                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1074                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1075                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1076                _ => ()1077            };10781079            Ok(())1080        }10811082        /// Set, change, or remove approved address to transfer the ownership of the NFT.1083        /// 1084        /// # Permissions1085        /// 1086        /// * Collection Owner1087        /// * Collection Admin1088        /// * Current NFT owner1089        /// 1090        /// # Arguments1091        /// 1092        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1093        /// 1094        /// * collection_id.1095        /// 1096        /// * item_id: ID of the item.1097        #[weight = T::WeightInfo::approve()]1098        pub fn approve(origin, approved: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {10991100            let sender = ensure_signed(origin)?;11011102            // Transfer permissions check1103            let target_collection = <Collection<T>>::get(collection_id);1104            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1105                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1106                Error::<T>::NoPermission);11071108            if target_collection.access == AccessMode::WhiteList {1109                Self::check_white_list(collection_id, &sender)?;1110                Self::check_white_list(collection_id, &approved)?;1111            }11121113            // amount param stub1114            let amount = 100000000;11151116            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1117            if list_exists {11181119                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1120                let item_contains = list.iter().any(|i| i.approved == approved);11211122                if !item_contains {1123                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1124                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1125                }1126            } else {11271128                let mut list = Vec::new();1129                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1130                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1131            }11321133            Ok(())1134        }1135        1136        /// 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.1137        /// 1138        /// # Permissions1139        /// * Collection Owner1140        /// * Collection Admin1141        /// * Current NFT owner1142        /// * Address approved by current NFT owner1143        /// 1144        /// # Arguments1145        /// 1146        /// * from: Address that owns token.1147        /// 1148        /// * recipient: Address of token recipient.1149        /// 1150        /// * collection_id.1151        /// 1152        /// * item_id: ID of the item.1153        /// 1154        /// * value: Amount to transfer.1155        #[weight = T::WeightInfo::transfer_from()]1156        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11571158            let sender = ensure_signed(origin)?;1159            let mut appoved_transfer = false;11601161            // Check approve1162            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1163                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1164                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1165                if opt_item.is_some()1166                {1167                    appoved_transfer = true;1168                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1169                }1170            }11711172            let target_collection = <Collection<T>>::get(collection_id);11731174            // Limits check1175            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11761177            // Transfer permissions check         1178            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1179            Error::<T>::NoPermission);11801181            if target_collection.access == AccessMode::WhiteList {1182                Self::check_white_list(collection_id, &sender)?;1183                Self::check_white_list(collection_id, &recipient)?;1184            }11851186            // remove approve1187            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1188                .into_iter().filter(|i| i.approved != sender.clone()).collect();1189            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);119011911192            match target_collection.mode1193            {1194                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1195                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1196                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1197                _ => ()1198            };11991200            Ok(())1201        }12021203        ///1204        #[weight = 0]1205        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {12061207            // let no_perm_mes = "You do not have permissions to modify this collection";1208            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1209            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1210            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);12111212            // // on_nft_received  call12131214            // Self::transfer(origin, collection_id, item_id, new_owner)?;12151216            Ok(())1217        }12181219        /// Set off-chain data schema.1220        /// 1221        /// # Permissions1222        /// 1223        /// * Collection Owner1224        /// * Collection Admin1225        /// 1226        /// # Arguments1227        /// 1228        /// * collection_id.1229        /// 1230        /// * schema: String representing the offchain data schema.1231        #[weight = T::WeightInfo::set_variable_meta_data()]1232        pub fn set_variable_meta_data (1233            origin,1234            collection_id: CollectionId,1235            item_id: TokenId,1236            data: Vec<u8>1237        ) -> DispatchResult {1238            let sender = ensure_signed(origin)?;1239            1240            Self::collection_exists(collection_id)?;1241            1242            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12431244            // Modify permissions check1245            let target_collection = <Collection<T>>::get(collection_id);1246            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1247                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1248                Error::<T>::NoPermission);12491250            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12511252            match target_collection.mode1253            {1254                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1255                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1256                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1257                _ => fail!(Error::<T>::UnexpectedCollectionType)1258            };12591260            Ok(())1261        }1262        12631264        /// Set off-chain data schema.1265        /// 1266        /// # Permissions1267        /// 1268        /// * Collection Owner1269        /// * Collection Admin1270        /// 1271        /// # Arguments1272        /// 1273        /// * collection_id.1274        /// 1275        /// * schema: String representing the offchain data schema.1276        #[weight = T::WeightInfo::set_offchain_schema()]1277        pub fn set_offchain_schema(1278            origin,1279            collection_id: CollectionId,1280            schema: Vec<u8>1281        ) -> DispatchResult {1282            let sender = ensure_signed(origin)?;1283            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12841285            let mut target_collection = <Collection<T>>::get(collection_id);1286            target_collection.offchain_schema = schema;1287            <Collection<T>>::insert(collection_id, target_collection);12881289            Ok(())1290        }12911292        /// Set const on-chain data schema.1293        /// 1294        /// # Permissions1295        /// 1296        /// * Collection Owner1297        /// * Collection Admin1298        /// 1299        /// # Arguments1300        /// 1301        /// * collection_id.1302        /// 1303        /// * schema: String representing the const on-chain data schema.1304        #[weight = T::WeightInfo::set_const_on_chain_schema()]1305        pub fn set_const_on_chain_schema (1306            origin,1307            collection_id: CollectionId,1308            schema: Vec<u8>1309        ) -> DispatchResult {1310            let sender = ensure_signed(origin)?;1311            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13121313            let mut target_collection = <Collection<T>>::get(collection_id);1314            target_collection.const_on_chain_schema = schema;1315            <Collection<T>>::insert(collection_id, target_collection);13161317            Ok(())1318        }13191320        /// Set variable on-chain data schema.1321        /// 1322        /// # Permissions1323        /// 1324        /// * Collection Owner1325        /// * Collection Admin1326        /// 1327        /// # Arguments1328        /// 1329        /// * collection_id.1330        /// 1331        /// * schema: String representing the variable on-chain data schema.1332        #[weight = T::WeightInfo::set_const_on_chain_schema()]1333        pub fn set_variable_on_chain_schema (1334            origin,1335            collection_id: CollectionId,1336            schema: Vec<u8>1337        ) -> DispatchResult {1338            let sender = ensure_signed(origin)?;1339            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13401341            let mut target_collection = <Collection<T>>::get(collection_id);1342            target_collection.variable_on_chain_schema = schema;1343            <Collection<T>>::insert(collection_id, target_collection);13441345            Ok(())1346        }13471348        // Sudo permissions function1349        #[weight = 0]1350        pub fn set_chain_limits(1351            origin,1352            limits: ChainLimits1353        ) -> DispatchResult {1354            ensure_root(origin)?;1355            <ChainLimit>::put(limits);1356            Ok(())1357        }13581359        /// Enable smart contract self-sponsoring.1360        /// 1361        /// # Permissions1362        /// 1363        /// * Contract Owner1364        /// 1365        /// # Arguments1366        /// 1367        /// * contract address1368        /// * enable flag1369        /// 1370        #[weight = T::WeightInfo::enable_contract_sponsoring()]1371        pub fn enable_contract_sponsoring(1372            origin,1373            contract_address: T::AccountId,1374            enable: bool1375        ) -> DispatchResult {13761377            let sender = ensure_signed(origin)?;13781379            #[cfg(feature = "runtime-benchmarks")]1380            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13811382            let mut is_owner = false;1383            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1384                let owner = <ContractOwner<T>>::get(&contract_address);1385                is_owner = sender == owner;1386            }1387            ensure!(is_owner, Error::<T>::NoPermission);13881389            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1390            Ok(())1391        }13921393        /// Set the rate limit for contract sponsoring to specified number of blocks.1394        /// 1395        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1396        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1397        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1398        /// from contract endowment if there are at least B blocks between such transactions. 1399        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1400        /// 1401        /// # Permissions1402        /// 1403        /// * Contract Owner1404        /// 1405        /// # Arguments1406        /// 1407        /// -`contract_address`: Address of the contract to sponsor1408        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1409        /// 1410        #[weight = 0]1411        pub fn set_contract_sponsoring_rate_limit(1412            origin,1413            contract_address: T::AccountId,1414            rate_limit: T::BlockNumber1415        ) -> DispatchResult {1416            let sender = ensure_signed(origin)?;1417            let mut is_owner = false;1418            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1419                let owner = <ContractOwner<T>>::get(&contract_address);1420                is_owner = sender == owner;1421            }1422            ensure!(is_owner, Error::<T>::NoPermission);14231424            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1425            Ok(())1426        }14271428        #[weight = 0]1429        pub fn set_collection_limits(1430            origin,1431            collection_id: u32,1432            limits: CollectionLimits,1433        ) -> DispatchResult {1434            let sender = ensure_signed(origin)?;1435            Self::check_owner_permissions(collection_id, sender.clone())?;1436            let mut target_collection = <Collection<T>>::get(collection_id);1437            let chain_limits = ChainLimit::get();1438            let climits = target_collection.limits;14391440            // collection bounds1441            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1442                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1443                Error::<T>::CollectionLimitBoundsExceeded);14441445            // token_limit   check  prev1446            ensure!(climits.token_limit > limits.token_limit && 1447                limits.token_limit <= chain_limits.account_token_ownership_limit, 1448                Error::<T>::AccountTokenLimitExceeded);14491450            target_collection.limits = limits;1451            <Collection<T>>::insert(collection_id, target_collection);14521453            Ok(())1454        } 1455    }1456}14571458impl<T: Trait> Module<T> {14591460    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {14611462        if !Self::is_owner_or_admin_permissions(collection_id, recipient.clone()) {14631464            // check token limit and account token limit1465            let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1466            ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1467        }14681469        Ok(())1470    }14711472    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {14731474        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {14751476            // check token limit and account token limit1477            let total_items: u32 = ItemListIndex::get(collection_id);1478            let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1479            ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1480            ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1481            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1482            Self::check_white_list(collection_id, owner)?;1483            Self::check_white_list(collection_id, sender)?;1484        }14851486        Ok(())1487    }14881489    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1490        match target_collection.mode1491        {1492            CollectionMode::NFT => {1493                if let CreateItemData::NFT(data) = data {1494                    // check sizes1495                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1496                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1497                } else {1498                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1499                }1500            },1501            CollectionMode::Fungible(_) => {1502                if let CreateItemData::Fungible(_) = data {1503                } else {1504                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1505                }1506            },1507            CollectionMode::ReFungible(_) => {1508                if let CreateItemData::ReFungible(data) = data {15091510                    // check sizes1511                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1512                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1513                } else {1514                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1515                }1516            },1517            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1518        };15191520        Ok(())1521    }15221523    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1524        match data1525        {1526            CreateItemData::NFT(data) => {1527                let item = NftItemType {1528                    collection: collection_id,1529                    owner,1530                    const_data: data.const_data,1531                    variable_data: data.variable_data1532                };15331534                Self::add_nft_item(item)?;1535            },1536            CreateItemData::Fungible(_) => {1537                let item = FungibleItemType {1538                    collection: collection_id,1539                    owner,1540                    value: (10 as u128).pow(collection.decimal_points as u32)1541                };15421543                Self::add_fungible_item(item)?;1544            },1545            CreateItemData::ReFungible(data) => {1546                let mut owner_list = Vec::new();1547                let value = (10 as u128).pow(collection.decimal_points as u32);1548                owner_list.push(Ownership {owner: owner.clone(), fraction: value});15491550                let item = ReFungibleItemType {1551                    collection: collection_id,1552                    owner: owner_list,1553                    const_data: data.const_data,1554                    variable_data: data.variable_data1555                };15561557                Self::add_refungible_item(item)?;1558            }1559        };15601561        // call event1562        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));15631564        Ok(())1565    }15661567    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1568        let current_index = <ItemListIndex>::get(item.collection)1569            .checked_add(1)1570            .ok_or(Error::<T>::NumOverflow)?;1571        let itemcopy = item.clone();1572        let owner = item.owner.clone();15731574        Self::add_token_index(item.collection, current_index, owner.clone())?;15751576        <ItemListIndex>::insert(item.collection, current_index);1577        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15781579        // Add current block1580        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1581        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1582        1583        // Update balance1584        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1585            .checked_add(item.value)1586            .ok_or(Error::<T>::NumOverflow)?;1587        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15881589        Ok(())1590    }15911592    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1593        let current_index = <ItemListIndex>::get(item.collection)1594            .checked_add(1)1595            .ok_or(Error::<T>::NumOverflow)?;1596        let itemcopy = item.clone();15971598        let value = item.owner.first().unwrap().fraction;1599        let owner = item.owner.first().unwrap().owner.clone();16001601        Self::add_token_index(item.collection, current_index, owner.clone())?;16021603        <ItemListIndex>::insert(item.collection, current_index);1604        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);16051606        // Add current block1607        let block_number: T::BlockNumber = 0.into();1608        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);16091610        // Update balance1611        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1612            .checked_add(value)1613            .ok_or(Error::<T>::NumOverflow)?;1614        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);16151616        Ok(())1617    }16181619    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1620        let current_index = <ItemListIndex>::get(item.collection)1621            .checked_add(1)1622            .ok_or(Error::<T>::NumOverflow)?;16231624        let item_owner = item.owner.clone();1625        let collection_id = item.collection.clone();1626        Self::add_token_index(collection_id, current_index, item.owner.clone())?;16271628        <ItemListIndex>::insert(collection_id, current_index);1629        <NftItemList<T>>::insert(collection_id, current_index, item);16301631        // Add current block1632        let block_number: T::BlockNumber = 0.into();1633        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);16341635        // Update balance1636        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1637            .checked_add(1)1638            .ok_or(Error::<T>::NumOverflow)?;1639        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);16401641        Ok(())1642    }16431644    fn burn_refungible_item(1645        collection_id: CollectionId,1646        item_id: TokenId,1647        owner: T::AccountId,1648    ) -> DispatchResult {1649        ensure!(1650            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1651            Error::<T>::TokenNotFound1652        );1653        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1654        let item = collection1655            .owner1656            .iter()1657            .filter(|&i| i.owner == owner)1658            .next()1659            .unwrap();1660        Self::remove_token_index(collection_id, item_id, owner.clone())?;16611662        // remove approve list1663        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));16641665        // update balance1666        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1667            .checked_sub(item.fraction)1668            .ok_or(Error::<T>::NumOverflow)?;1669        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);16701671        <ReFungibleItemList<T>>::remove(collection_id, item_id);16721673        Ok(())1674    }16751676    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1677        ensure!(1678            <NftItemList<T>>::contains_key(collection_id, item_id),1679            Error::<T>::TokenNotFound1680        );1681        let item = <NftItemList<T>>::get(collection_id, item_id);1682        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16831684        // remove approve list1685        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16861687        // update balance1688        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1689            .checked_sub(1)1690            .ok_or(Error::<T>::NumOverflow)?;1691        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1692        <NftItemList<T>>::remove(collection_id, item_id);16931694        Ok(())1695    }16961697    fn burn_fungible_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1698        ensure!(1699            <FungibleItemList<T>>::contains_key(collection_id, item_id),1700            Error::<T>::TokenNotFound1701        );1702        let item = <FungibleItemList<T>>::get(collection_id, item_id);1703        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17041705        // remove approve list1706        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));17071708        // update balance1709        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1710            .checked_sub(item.value)1711            .ok_or(Error::<T>::NumOverflow)?;1712        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17131714        <FungibleItemList<T>>::remove(collection_id, item_id);17151716        Ok(())1717    }17181719    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1720        ensure!(1721            <Collection<T>>::contains_key(collection_id),1722            Error::<T>::CollectionNotFound1723        );1724        Ok(())1725    }17261727    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1728        Self::collection_exists(collection_id)?;17291730        let target_collection = <Collection<T>>::get(collection_id);1731        ensure!(1732            subject == target_collection.owner,1733            Error::<T>::NoPermission1734        );17351736        Ok(())1737    }17381739    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1740        let target_collection = <Collection<T>>::get(collection_id);1741        let mut result: bool = subject == target_collection.owner;1742        let exists = <AdminList<T>>::contains_key(collection_id);17431744        if !result & exists {1745            if <AdminList<T>>::get(collection_id).contains(&subject) {1746                result = true1747            }1748        }17491750        result1751    }17521753    fn check_owner_or_admin_permissions(1754        collection_id: CollectionId,1755        subject: T::AccountId,1756    ) -> DispatchResult {1757        Self::collection_exists(collection_id)?;1758        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());17591760        ensure!(1761            result,1762            Error::<T>::NoPermission1763        );1764        Ok(())1765    }17661767    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1768        let target_collection = <Collection<T>>::get(collection_id);17691770        match target_collection.mode {1771            CollectionMode::NFT => {1772                <NftItemList<T>>::get(collection_id, item_id).owner == subject1773            }1774            CollectionMode::Fungible(_) => {1775                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1776            }1777            CollectionMode::ReFungible(_) => {1778                <ReFungibleItemList<T>>::get(collection_id, item_id)1779                    .owner1780                    .iter()1781                    .any(|i| i.owner == subject)1782            }1783            CollectionMode::Invalid => false,1784        }1785    }17861787    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1788        let mes = Error::<T>::AddresNotInWhiteList;1789        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1790        let wl = <WhiteList<T>>::get(collection_id);1791        ensure!(wl.contains(address), mes);17921793        Ok(())1794    }17951796    fn transfer_fungible(1797        collection_id: CollectionId,1798        item_id: TokenId,1799        value: u128,1800        owner: T::AccountId,1801        new_owner: T::AccountId,1802    ) -> DispatchResult {1803        ensure!(1804            <FungibleItemList<T>>::contains_key(collection_id, item_id),1805            Error::<T>::TokenNotFound1806        );18071808        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1809        let amount = full_item.value;18101811        ensure!(amount >= value, Error::<T>::TokenValueTooLow);18121813        // update balance1814        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1815            .checked_sub(value)1816            .ok_or(Error::<T>::NumOverflow)?;1817        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);18181819        let mut new_owner_account_id = 0;1820        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1821        if new_owner_items.len() > 0 {1822            new_owner_account_id = new_owner_items[0];1823        }18241825        // transfer1826        if amount == value && new_owner_account_id == 0 {1827            // change owner1828            // new owner do not have account1829            let mut new_full_item = full_item.clone();1830            new_full_item.owner = new_owner.clone();1831            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18321833            // update balance1834            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1835                .checked_add(value)1836                .ok_or(Error::<T>::NumOverflow)?;1837            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18381839            // update index collection1840            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1841        } else {1842            let mut new_full_item = full_item.clone();1843            new_full_item.value -= value;18441845            // separate amount1846            if new_owner_account_id > 0 {1847                // new owner has account1848                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1849                item.value += value;18501851                // update balance1852                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1853                    .checked_add(value)1854                    .ok_or(Error::<T>::NumOverflow)?;1855                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18561857                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1858            } else {1859                // new owner do not have account1860                let item = FungibleItemType {1861                    collection: collection_id,1862                    owner: new_owner.clone(),1863                    value1864                };18651866                Self::add_fungible_item(item)?;1867            }18681869            if amount == value {1870                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;18711872                // remove approve list1873                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1874                <FungibleItemList<T>>::remove(collection_id, item_id);1875            }18761877            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1878        }18791880        Ok(())1881    }18821883    fn transfer_refungible(1884        collection_id: CollectionId,1885        item_id: TokenId,1886        value: u128,1887        owner: T::AccountId,1888        new_owner: T::AccountId,1889    ) -> DispatchResult {1890        ensure!(1891            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1892            Error::<T>::TokenNotFound1893        );18941895        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1896        let item = full_item1897            .owner1898            .iter()1899            .filter(|i| i.owner == owner)1900            .next()1901            .ok_or(Error::<T>::NumOverflow)?;1902        let amount = item.fraction;19031904        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19051906        // update balance1907        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1908            .checked_sub(value)1909            .ok_or(Error::<T>::NumOverflow)?;1910        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19111912        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1913            .checked_add(value)1914            .ok_or(Error::<T>::NumOverflow)?;1915        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19161917        let old_owner = item.owner.clone();1918        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19191920        // transfer1921        if amount == value && !new_owner_has_account {1922            // change owner1923            // new owner do not have account1924            let mut new_full_item = full_item.clone();1925            new_full_item1926                .owner1927                .iter_mut()1928                .find(|i| i.owner == owner)1929                .unwrap()1930                .owner = new_owner.clone();1931            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19321933            // update index collection1934            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1935        } else {1936            let mut new_full_item = full_item.clone();1937            new_full_item1938                .owner1939                .iter_mut()1940                .find(|i| i.owner == owner)1941                .unwrap()1942                .fraction -= value;19431944            // separate amount1945            if new_owner_has_account {1946                // new owner has account1947                new_full_item1948                    .owner1949                    .iter_mut()1950                    .find(|i| i.owner == new_owner)1951                    .unwrap()1952                    .fraction += value;1953            } else {1954                // new owner do not have account1955                new_full_item.owner.push(Ownership {1956                    owner: new_owner.clone(),1957                    fraction: value,1958                });1959                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1960            }19611962            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1963        }19641965        Ok(())1966    }19671968    fn transfer_nft(1969        collection_id: CollectionId,1970        item_id: TokenId,1971        sender: T::AccountId,1972        new_owner: T::AccountId,1973    ) -> DispatchResult {1974        ensure!(1975            <NftItemList<T>>::contains_key(collection_id, item_id),1976            Error::<T>::TokenNotFound1977        );19781979        let mut item = <NftItemList<T>>::get(collection_id, item_id);19801981        ensure!(1982            sender == item.owner,1983            Error::<T>::MustBeTokenOwner1984        );19851986        // update balance1987        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1988            .checked_sub(1)1989            .ok_or(Error::<T>::NumOverflow)?;1990        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19911992        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1993            .checked_add(1)1994            .ok_or(Error::<T>::NumOverflow)?;1995        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19961997        // change owner1998        let old_owner = item.owner.clone();1999        item.owner = new_owner.clone();2000        <NftItemList<T>>::insert(collection_id, item_id, item);20012002        // update index collection2003        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;20042005        // reset approved list2006        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));2007        Ok(())2008    }2009    2010    fn item_exists(2011        collection_id: CollectionId,2012        item_id: TokenId,2013        mode: &CollectionMode2014    ) -> DispatchResult {2015        match mode {2016            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2017            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2018            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2019            _ => ()2020        };2021        2022        Ok(())2023    }20242025    fn set_re_fungible_variable_data(2026        collection_id: CollectionId,2027        item_id: TokenId,2028        data: Vec<u8>2029    ) -> DispatchResult {2030        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20312032        item.variable_data = data;20332034        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20352036        Ok(())2037    }20382039    fn set_nft_variable_data(2040        collection_id: CollectionId,2041        item_id: TokenId,2042        data: Vec<u8>2043    ) -> DispatchResult {2044        let mut item = <NftItemList<T>>::get(collection_id, item_id);2045        2046        item.variable_data = data;20472048        <NftItemList<T>>::insert(collection_id, item_id, item);2049        2050        Ok(())2051    }20522053    fn init_collection(item: &CollectionType<T::AccountId>) {2054        // check params2055        assert!(2056            item.decimal_points <= MAX_DECIMAL_POINTS,2057            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2058        );2059        assert!(2060            item.name.len() <= 64,2061            "Collection name can not be longer than 63 char"2062        );2063        assert!(2064            item.name.len() <= 256,2065            "Collection description can not be longer than 255 char"2066        );2067        assert!(2068            item.token_prefix.len() <= 16,2069            "Token prefix can not be longer than 15 char"2070        );20712072        // Generate next collection ID2073        let next_id = CreatedCollectionCount::get()2074            .checked_add(1)2075            .unwrap();20762077        CreatedCollectionCount::put(next_id);2078    }20792080    fn init_nft_token(item: &NftItemType<T::AccountId>) {2081        let current_index = <ItemListIndex>::get(item.collection)2082            .checked_add(1)2083            .unwrap();20842085        let item_owner = item.owner.clone();2086        let collection_id = item.collection.clone();2087        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();20882089        <ItemListIndex>::insert(collection_id, current_index);20902091        // Update balance2092        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2093            .checked_add(1)2094            .unwrap();2095        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2096    }20972098    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2099        let current_index = <ItemListIndex>::get(item.collection)2100            .checked_add(1)2101            .unwrap();2102        let owner = item.owner.clone();21032104        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();21052106        <ItemListIndex>::insert(item.collection, current_index);21072108        // Update balance2109        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2110            .checked_add(item.value)2111            .unwrap();2112        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2113    }21142115    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2116        let current_index = <ItemListIndex>::get(item.collection)2117            .checked_add(1)2118            .unwrap();21192120        let value = item.owner.first().unwrap().fraction;2121        let owner = item.owner.first().unwrap().owner.clone();21222123        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();21242125        <ItemListIndex>::insert(item.collection, current_index);21262127        // Update balance2128        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2129            .checked_add(value)2130            .unwrap();2131        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2132    }21332134    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {21352136        // add to account limit2137        if <AccountItemCount<T>>::contains_key(owner.clone()) {21382139            // bound Owned tokens by a single address2140            let count = <AccountItemCount<T>>::get(owner.clone());2141            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21422143            <AccountItemCount<T>>::insert(owner.clone(), count2144                .checked_add(1)2145                .ok_or(Error::<T>::NumOverflow)?);2146        }2147        else {2148            <AccountItemCount<T>>::insert(owner.clone(), 1);2149        }21502151        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2152        if list_exists {2153            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2154            let item_contains = list.contains(&item_index.clone());21552156            if !item_contains {2157                list.push(item_index.clone());2158            }21592160            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2161        } else {2162            let mut itm = Vec::new();2163            itm.push(item_index.clone());2164            <AddressTokens<T>>::insert(collection_id, owner, itm);2165            2166        }21672168        Ok(())2169    }21702171    fn remove_token_index(2172        collection_id: CollectionId,2173        item_index: TokenId,2174        owner: T::AccountId,2175    ) -> DispatchResult {21762177        // update counter2178        <AccountItemCount<T>>::insert(owner.clone(), 2179            <AccountItemCount<T>>::get(owner.clone())2180            .checked_sub(1)2181            .ok_or(Error::<T>::NumOverflow)?);218221832184        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2185        if list_exists {2186            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2187            let item_contains = list.contains(&item_index.clone());21882189            if item_contains {2190                list.retain(|&item| item != item_index);2191                <AddressTokens<T>>::insert(collection_id, owner, list);2192            }2193        }21942195        Ok(())2196    }21972198    fn move_token_index(2199        collection_id: CollectionId,2200        item_index: TokenId,2201        old_owner: T::AccountId,2202        new_owner: T::AccountId,2203    ) -> DispatchResult {2204        Self::remove_token_index(collection_id, item_index, old_owner)?;2205        Self::add_token_index(collection_id, item_index, new_owner)?;22062207        Ok(())2208    }2209}22102211////////////////////////////////////////////////////////////////////////////////////////////////////2212// Economic models2213// #region22142215/// Fee multiplier.2216pub type Multiplier = FixedU128;22172218type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2219    <T as system::Trait>::AccountId,2220>>::Balance;2221type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2222    <T as system::Trait>::AccountId,2223>>::NegativeImbalance;22242225/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2226/// in the queue.2227#[derive(Encode, Decode, Clone, Eq, PartialEq)]2228pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2229    #[codec(compact)] BalanceOf<T>2230);22312232impl<T: Trait + Send + Sync> sp_std::fmt::Debug2233    for ChargeTransactionPayment<T>2234{2235    #[cfg(feature = "std")]2236    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2237        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2238    }2239    #[cfg(not(feature = "std"))]2240    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2241        Ok(())2242    }2243}22442245impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2246where2247    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2248    BalanceOf<T>: Send + Sync + FixedPointOperand,2249{2250    /// utility constructor. Used only in client/factory code.2251    pub fn from(fee: BalanceOf<T>) -> Self {2252        Self(fee)2253    }22542255    pub fn traditional_fee(2256        len: usize,2257        info: &DispatchInfoOf<T::Call>,2258        tip: BalanceOf<T>,2259    ) -> BalanceOf<T>2260    where2261        T::Call: Dispatchable<Info = DispatchInfo>,2262    {2263        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2264    }22652266    fn withdraw_fee(2267        &self,2268        who: &T::AccountId,2269        call: &T::Call,2270        info: &DispatchInfoOf<T::Call>,2271        len: usize,2272    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2273        let tip = self.0;22742275        // Set fee based on call type. Creating collection costs 1 Unique.2276        // All other transactions have traditional fees so far2277        // let fee = match call.is_sub_type() {2278        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2279        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2280        //                                                 // _ => <BalanceOf<T>>::from(100)2281        // };2282        let fee = Self::traditional_fee(len, info, tip);22832284        // Determine who is paying transaction fee based on ecnomic model2285        // Parse call to extract collection ID and access collection sponsor2286        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2287            Some(Call::create_item(collection_id, _owner, _properties)) => {22882289                // check free create limit2290                if <Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)2291                {2292                    <Collection<T>>::get(collection_id).sponsor2293                } else {2294                    T::AccountId::default()2295                }2296            }2297            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2298                2299                let _collection_limits = <Collection<T>>::get(collection_id).limits;2300                let _collection_mode = <Collection<T>>::get(collection_id).mode;23012302                // sponsor timeout2303                let sponsor_transfer = match _collection_mode {2304                    CollectionMode::NFT => {23052306                        // get correct limit2307                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2308                            _collection_limits.sponsor_transfer_timeout2309                        } else {2310                            ChainLimit::get().nft_sponsor_transfer_timeout2311                        };23122313                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2314                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2315                        let limit_time = basket + limit.into();2316                        if block_number >= limit_time {2317                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2318                            true2319                        }2320                        else {2321                            false2322                        }2323                    }2324                    CollectionMode::Fungible(_) => {23252326                        // get correct limit2327                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2328                            _collection_limits.sponsor_transfer_timeout2329                        } else {2330                            ChainLimit::get().fungible_sponsor_transfer_timeout2331                        };23322333                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2334                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2335                        if basket.iter().any(|i| i.address == _new_owner.clone())2336                        {2337                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2338                            let limit_time = item.start_block + limit.into();2339                            if block_number >= limit_time {2340                                basket.retain(|x| x.address == item.address);2341                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2342                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2343                                true2344                            }2345                            else {2346                                false2347                            }2348                        }2349                        else {2350                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2351                            true2352                        }2353                    }2354                    CollectionMode::ReFungible(_) => {23552356                        // get correct limit2357                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2358                            _collection_limits.sponsor_transfer_timeout2359                        } else {2360                            ChainLimit::get().refungible_sponsor_transfer_timeout2361                        };23622363                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2364                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2365                        let limit_time = basket + limit.into();2366                        if block_number >= limit_time {2367                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2368                            true2369                        } else {2370                            false2371                        }2372                    }2373                    _ => {2374                        false2375                    },2376                };23772378                if !sponsor_transfer {2379                    T::AccountId::default()2380                } else {2381                    <Collection<T>>::get(collection_id).sponsor2382                }2383            }23842385            _ => T::AccountId::default(),2386        };23872388        // Sponsor smart contracts2389        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {23902391            // On instantiation: set the contract owner2392            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {23932394                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2395                    code_hash,2396                    &data,2397                    &who,2398                );2399                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());24002401                T::AccountId::default()2402            },24032404            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2405            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {24062407                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());24082409                let mut sponsor_transfer = false;2410                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2411                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2412                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2413                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2414                    let limit_time = last_tx_block + rate_limit;24152416                    if block_number >= limit_time {2417                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2418                        sponsor_transfer = true;2419                    }2420                } else {2421                    sponsor_transfer = false;2422                }2423               2424                2425                let mut sp = T::AccountId::default();2426                if sponsor_transfer {2427                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2428                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2429                            sp = called_contract;2430                        }2431                    }2432                }24332434                sp2435            },24362437            _ => sponsor,2438        };24392440        let mut who_pays_fee: T::AccountId = sponsor.clone();2441        if sponsor == T::AccountId::default() {2442            who_pays_fee = who.clone();2443        }24442445        // Only mess with balances if fee is not zero.2446        if fee.is_zero() {2447            return Ok((fee, None));2448        }24492450        match <T as transaction_payment::Trait>::Currency::withdraw(2451            &who_pays_fee,2452            fee,2453            if tip.is_zero() {2454                WithdrawReason::TransactionPayment.into()2455            } else {2456                WithdrawReason::TransactionPayment | WithdrawReason::Tip2457            },2458            ExistenceRequirement::KeepAlive,2459        ) {2460            Ok(imbalance) => Ok((fee, Some(imbalance))),2461            Err(_) => Err(InvalidTransaction::Payment.into()),2462        }2463    }2464}246524662467impl<T: Trait + Send + Sync> SignedExtension2468    for ChargeTransactionPayment<T>2469where2470    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2471    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2472{2473    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2474    type AccountId = T::AccountId;2475    type Call = T::Call;2476    type AdditionalSigned = ();2477    type Pre = (2478        BalanceOf<T>,2479        Self::AccountId,2480        Option<NegativeImbalanceOf<T>>,2481        BalanceOf<T>,2482    );2483    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2484        Ok(())2485    }24862487    fn validate(2488        &self,2489        _who: &Self::AccountId,2490        _call: &Self::Call,2491        _info: &DispatchInfoOf<Self::Call>,2492        _len: usize,2493    ) -> TransactionValidity {2494        Ok(ValidTransaction::default())2495    }24962497    fn pre_dispatch(2498        self,2499        who: &Self::AccountId,2500        call: &Self::Call,2501        info: &DispatchInfoOf<Self::Call>,2502        len: usize,2503    ) -> Result<Self::Pre, TransactionValidityError> {2504        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2505        Ok((self.0, who.clone(), imbalance, fee))2506    }25072508    fn post_dispatch(2509        pre: Self::Pre,2510        info: &DispatchInfoOf<Self::Call>,2511        post_info: &PostDispatchInfoOf<Self::Call>,2512        len: usize,2513        _result: &DispatchResult,2514    ) -> Result<(), TransactionValidityError> {2515        let (tip, who, imbalance, fee) = pre;2516        if let Some(payed) = imbalance {2517            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2518                len as u32, info, post_info, tip,2519            );2520            let refund = fee.saturating_sub(actual_fee);2521            let actual_payment =2522                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2523                    &who, refund,2524                ) {2525                    Ok(refund_imbalance) => {2526                        // The refund cannot be larger than the up front payed max weight.2527                        // `PostDispatchInfo::calc_unspent` guards against such a case.2528                        match payed.offset(refund_imbalance) {2529                            Ok(actual_payment) => actual_payment,2530                            Err(_) => return Err(InvalidTransaction::Payment.into()),2531                        }2532                    }2533                    // We do not recreate the account using the refund. The up front payment2534                    // is gone in that case.2535                    Err(_) => payed,2536                };2537            let imbalances = actual_payment.split(tip);2538            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2539                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2540            );2541        }2542        Ok(())2543    }2544}25452546// #endregion