git.delta.rocks / unique-network / refs/commits / 944d507c1d0c

difftreelog

NFTPAR-164. Schema version

str-mv2020-12-17parent: #f46510a.patch.diff
in: master

4 files changed

modifiednode/Cargo.tomldiffbeforeafterboth
--- a/node/Cargo.toml
+++ b/node/Cargo.toml
@@ -23,6 +23,7 @@
 [dependencies]
 futures = '0.3.4'
 log = '0.4.8'
+flexi_logger = "0.15.7"
 parking_lot = '0.10.0'
 structopt = '0.3.8'
 jsonrpc-core = '15.0.0'
modifiednode/src/chain_spec.rsdiffbeforeafterboth
--- a/node/src/chain_spec.rs
+++ b/node/src/chain_spec.rs
@@ -167,7 +167,8 @@
                     description: vec![],
                     token_prefix: vec![],
                     mint_mode: false,
-                    offchain_schema: vec![],
+					offchain_schema: vec![],
+					schema_version: SchemaVersion::default(),
                     sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),
                     unconfirmed_sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),
                     const_on_chain_schema: vec![],
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;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 {1461        1462        // check token limit and account token limit1463        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1464        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1465        1466        Ok(())1467    }14681469    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {14701471        // check token limit and account token limit1472        let total_items: u32 = ItemListIndex::get(collection_id);1473        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1474        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1475        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);14761477        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1478            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1479            Self::check_white_list(collection_id, owner)?;1480            Self::check_white_list(collection_id, sender)?;1481        }14821483        Ok(())1484    }14851486    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1487        match target_collection.mode1488        {1489            CollectionMode::NFT => {1490                if let CreateItemData::NFT(data) = data {1491                    // check sizes1492                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1493                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1494                } else {1495                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1496                }1497            },1498            CollectionMode::Fungible(_) => {1499                if let CreateItemData::Fungible(_) = data {1500                } else {1501                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1502                }1503            },1504            CollectionMode::ReFungible(_) => {1505                if let CreateItemData::ReFungible(data) = data {15061507                    // check sizes1508                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1509                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1510                } else {1511                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1512                }1513            },1514            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1515        };15161517        Ok(())1518    }15191520    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1521        match data1522        {1523            CreateItemData::NFT(data) => {1524                let item = NftItemType {1525                    collection: collection_id,1526                    owner,1527                    const_data: data.const_data,1528                    variable_data: data.variable_data1529                };15301531                Self::add_nft_item(item)?;1532            },1533            CreateItemData::Fungible(_) => {1534                let item = FungibleItemType {1535                    collection: collection_id,1536                    owner,1537                    value: (10 as u128).pow(collection.decimal_points as u32)1538                };15391540                Self::add_fungible_item(item)?;1541            },1542            CreateItemData::ReFungible(data) => {1543                let mut owner_list = Vec::new();1544                let value = (10 as u128).pow(collection.decimal_points as u32);1545                owner_list.push(Ownership {owner: owner.clone(), fraction: value});15461547                let item = ReFungibleItemType {1548                    collection: collection_id,1549                    owner: owner_list,1550                    const_data: data.const_data,1551                    variable_data: data.variable_data1552                };15531554                Self::add_refungible_item(item)?;1555            }1556        };15571558        // call event1559        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));15601561        Ok(())1562    }15631564    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1565        let current_index = <ItemListIndex>::get(item.collection)1566            .checked_add(1)1567            .ok_or(Error::<T>::NumOverflow)?;1568        let itemcopy = item.clone();1569        let owner = item.owner.clone();15701571        Self::add_token_index(item.collection, current_index, owner.clone())?;15721573        <ItemListIndex>::insert(item.collection, current_index);1574        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15751576        // Add current block1577        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1578        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1579        1580        // Update balance1581        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1582            .checked_add(item.value)1583            .ok_or(Error::<T>::NumOverflow)?;1584        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15851586        Ok(())1587    }15881589    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1590        let current_index = <ItemListIndex>::get(item.collection)1591            .checked_add(1)1592            .ok_or(Error::<T>::NumOverflow)?;1593        let itemcopy = item.clone();15941595        let value = item.owner.first().unwrap().fraction;1596        let owner = item.owner.first().unwrap().owner.clone();15971598        Self::add_token_index(item.collection, current_index, owner.clone())?;15991600        <ItemListIndex>::insert(item.collection, current_index);1601        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);16021603        // Add current block1604        let block_number: T::BlockNumber = 0.into();1605        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);16061607        // Update balance1608        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1609            .checked_add(value)1610            .ok_or(Error::<T>::NumOverflow)?;1611        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);16121613        Ok(())1614    }16151616    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1617        let current_index = <ItemListIndex>::get(item.collection)1618            .checked_add(1)1619            .ok_or(Error::<T>::NumOverflow)?;16201621        let item_owner = item.owner.clone();1622        let collection_id = item.collection.clone();1623        Self::add_token_index(collection_id, current_index, item.owner.clone())?;16241625        <ItemListIndex>::insert(collection_id, current_index);1626        <NftItemList<T>>::insert(collection_id, current_index, item);16271628        // Add current block1629        let block_number: T::BlockNumber = 0.into();1630        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);16311632        // Update balance1633        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1634            .checked_add(1)1635            .ok_or(Error::<T>::NumOverflow)?;1636        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);16371638        Ok(())1639    }16401641    fn burn_refungible_item(1642        collection_id: CollectionId,1643        item_id: TokenId,1644        owner: T::AccountId,1645    ) -> DispatchResult {1646        ensure!(1647            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1648            Error::<T>::TokenNotFound1649        );1650        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1651        let item = collection1652            .owner1653            .iter()1654            .filter(|&i| i.owner == owner)1655            .next()1656            .unwrap();1657        Self::remove_token_index(collection_id, item_id, owner.clone())?;16581659        // remove approve list1660        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));16611662        // update balance1663        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1664            .checked_sub(item.fraction)1665            .ok_or(Error::<T>::NumOverflow)?;1666        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);16671668        <ReFungibleItemList<T>>::remove(collection_id, item_id);16691670        Ok(())1671    }16721673    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1674        ensure!(1675            <NftItemList<T>>::contains_key(collection_id, item_id),1676            Error::<T>::TokenNotFound1677        );1678        let item = <NftItemList<T>>::get(collection_id, item_id);1679        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16801681        // remove approve list1682        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16831684        // update balance1685        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1686            .checked_sub(1)1687            .ok_or(Error::<T>::NumOverflow)?;1688        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1689        <NftItemList<T>>::remove(collection_id, item_id);16901691        Ok(())1692    }16931694    fn burn_fungible_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1695        ensure!(1696            <FungibleItemList<T>>::contains_key(collection_id, item_id),1697            Error::<T>::TokenNotFound1698        );1699        let item = <FungibleItemList<T>>::get(collection_id, item_id);1700        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17011702        // remove approve list1703        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));17041705        // update balance1706        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1707            .checked_sub(item.value)1708            .ok_or(Error::<T>::NumOverflow)?;1709        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17101711        <FungibleItemList<T>>::remove(collection_id, item_id);17121713        Ok(())1714    }17151716    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1717        ensure!(1718            <Collection<T>>::contains_key(collection_id),1719            Error::<T>::CollectionNotFound1720        );1721        Ok(())1722    }17231724    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1725        Self::collection_exists(collection_id)?;17261727        let target_collection = <Collection<T>>::get(collection_id);1728        ensure!(1729            subject == target_collection.owner,1730            Error::<T>::NoPermission1731        );17321733        Ok(())1734    }17351736    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1737        let target_collection = <Collection<T>>::get(collection_id);1738        let mut result: bool = subject == target_collection.owner;1739        let exists = <AdminList<T>>::contains_key(collection_id);17401741        if !result & exists {1742            if <AdminList<T>>::get(collection_id).contains(&subject) {1743                result = true1744            }1745        }17461747        result1748    }17491750    fn check_owner_or_admin_permissions(1751        collection_id: CollectionId,1752        subject: T::AccountId,1753    ) -> DispatchResult {1754        Self::collection_exists(collection_id)?;1755        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());17561757        ensure!(1758            result,1759            Error::<T>::NoPermission1760        );1761        Ok(())1762    }17631764    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1765        let target_collection = <Collection<T>>::get(collection_id);17661767        match target_collection.mode {1768            CollectionMode::NFT => {1769                <NftItemList<T>>::get(collection_id, item_id).owner == subject1770            }1771            CollectionMode::Fungible(_) => {1772                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1773            }1774            CollectionMode::ReFungible(_) => {1775                <ReFungibleItemList<T>>::get(collection_id, item_id)1776                    .owner1777                    .iter()1778                    .any(|i| i.owner == subject)1779            }1780            CollectionMode::Invalid => false,1781        }1782    }17831784    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1785        let mes = Error::<T>::AddresNotInWhiteList;1786        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1787        let wl = <WhiteList<T>>::get(collection_id);1788        ensure!(wl.contains(address), mes);17891790        Ok(())1791    }17921793    fn transfer_fungible(1794        collection_id: CollectionId,1795        item_id: TokenId,1796        value: u128,1797        owner: T::AccountId,1798        new_owner: T::AccountId,1799    ) -> DispatchResult {1800        ensure!(1801            <FungibleItemList<T>>::contains_key(collection_id, item_id),1802            Error::<T>::TokenNotFound1803        );18041805        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1806        let amount = full_item.value;18071808        ensure!(amount >= value, Error::<T>::TokenValueTooLow);18091810        // update balance1811        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1812            .checked_sub(value)1813            .ok_or(Error::<T>::NumOverflow)?;1814        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);18151816        let mut new_owner_account_id = 0;1817        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1818        if new_owner_items.len() > 0 {1819            new_owner_account_id = new_owner_items[0];1820        }18211822        // transfer1823        if amount == value && new_owner_account_id == 0 {1824            // change owner1825            // new owner do not have account1826            let mut new_full_item = full_item.clone();1827            new_full_item.owner = new_owner.clone();1828            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18291830            // update balance1831            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1832                .checked_add(value)1833                .ok_or(Error::<T>::NumOverflow)?;1834            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18351836            // update index collection1837            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1838        } else {1839            let mut new_full_item = full_item.clone();1840            new_full_item.value -= value;18411842            // separate amount1843            if new_owner_account_id > 0 {1844                // new owner has account1845                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1846                item.value += value;18471848                // update balance1849                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1850                    .checked_add(value)1851                    .ok_or(Error::<T>::NumOverflow)?;1852                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18531854                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1855            } else {1856                // new owner do not have account1857                let item = FungibleItemType {1858                    collection: collection_id,1859                    owner: new_owner.clone(),1860                    value1861                };18621863                Self::add_fungible_item(item)?;1864            }18651866            if amount == value {1867                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;18681869                // remove approve list1870                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1871                <FungibleItemList<T>>::remove(collection_id, item_id);1872            }18731874            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1875        }18761877        Ok(())1878    }18791880    fn transfer_refungible(1881        collection_id: CollectionId,1882        item_id: TokenId,1883        value: u128,1884        owner: T::AccountId,1885        new_owner: T::AccountId,1886    ) -> DispatchResult {1887        ensure!(1888            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1889            Error::<T>::TokenNotFound1890        );18911892        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1893        let item = full_item1894            .owner1895            .iter()1896            .filter(|i| i.owner == owner)1897            .next()1898            .ok_or(Error::<T>::NumOverflow)?;1899        let amount = item.fraction;19001901        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19021903        // update balance1904        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1905            .checked_sub(value)1906            .ok_or(Error::<T>::NumOverflow)?;1907        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19081909        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1910            .checked_add(value)1911            .ok_or(Error::<T>::NumOverflow)?;1912        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19131914        let old_owner = item.owner.clone();1915        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19161917        // transfer1918        if amount == value && !new_owner_has_account {1919            // change owner1920            // new owner do not have account1921            let mut new_full_item = full_item.clone();1922            new_full_item1923                .owner1924                .iter_mut()1925                .find(|i| i.owner == owner)1926                .unwrap()1927                .owner = new_owner.clone();1928            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19291930            // update index collection1931            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1932        } else {1933            let mut new_full_item = full_item.clone();1934            new_full_item1935                .owner1936                .iter_mut()1937                .find(|i| i.owner == owner)1938                .unwrap()1939                .fraction -= value;19401941            // separate amount1942            if new_owner_has_account {1943                // new owner has account1944                new_full_item1945                    .owner1946                    .iter_mut()1947                    .find(|i| i.owner == new_owner)1948                    .unwrap()1949                    .fraction += value;1950            } else {1951                // new owner do not have account1952                new_full_item.owner.push(Ownership {1953                    owner: new_owner.clone(),1954                    fraction: value,1955                });1956                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1957            }19581959            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1960        }19611962        Ok(())1963    }19641965    fn transfer_nft(1966        collection_id: CollectionId,1967        item_id: TokenId,1968        sender: T::AccountId,1969        new_owner: T::AccountId,1970    ) -> DispatchResult {1971        ensure!(1972            <NftItemList<T>>::contains_key(collection_id, item_id),1973            Error::<T>::TokenNotFound1974        );19751976        let mut item = <NftItemList<T>>::get(collection_id, item_id);19771978        ensure!(1979            sender == item.owner,1980            Error::<T>::MustBeTokenOwner1981        );19821983        // update balance1984        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1985            .checked_sub(1)1986            .ok_or(Error::<T>::NumOverflow)?;1987        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19881989        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1990            .checked_add(1)1991            .ok_or(Error::<T>::NumOverflow)?;1992        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19931994        // change owner1995        let old_owner = item.owner.clone();1996        item.owner = new_owner.clone();1997        <NftItemList<T>>::insert(collection_id, item_id, item);19981999        // update index collection2000        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;20012002        // reset approved list2003        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));2004        Ok(())2005    }2006    2007    fn item_exists(2008        collection_id: CollectionId,2009        item_id: TokenId,2010        mode: &CollectionMode2011    ) -> DispatchResult {2012        match mode {2013            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2014            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2015            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2016            _ => ()2017        };2018        2019        Ok(())2020    }20212022    fn set_re_fungible_variable_data(2023        collection_id: CollectionId,2024        item_id: TokenId,2025        data: Vec<u8>2026    ) -> DispatchResult {2027        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20282029        item.variable_data = data;20302031        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20322033        Ok(())2034    }20352036    fn set_nft_variable_data(2037        collection_id: CollectionId,2038        item_id: TokenId,2039        data: Vec<u8>2040    ) -> DispatchResult {2041        let mut item = <NftItemList<T>>::get(collection_id, item_id);2042        2043        item.variable_data = data;20442045        <NftItemList<T>>::insert(collection_id, item_id, item);2046        2047        Ok(())2048    }20492050    fn init_collection(item: &CollectionType<T::AccountId>) {2051        // check params2052        assert!(2053            item.decimal_points <= MAX_DECIMAL_POINTS,2054            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2055        );2056        assert!(2057            item.name.len() <= 64,2058            "Collection name can not be longer than 63 char"2059        );2060        assert!(2061            item.name.len() <= 256,2062            "Collection description can not be longer than 255 char"2063        );2064        assert!(2065            item.token_prefix.len() <= 16,2066            "Token prefix can not be longer than 15 char"2067        );20682069        // Generate next collection ID2070        let next_id = CreatedCollectionCount::get()2071            .checked_add(1)2072            .unwrap();20732074        CreatedCollectionCount::put(next_id);2075    }20762077    fn init_nft_token(item: &NftItemType<T::AccountId>) {2078        let current_index = <ItemListIndex>::get(item.collection)2079            .checked_add(1)2080            .unwrap();20812082        let item_owner = item.owner.clone();2083        let collection_id = item.collection.clone();2084        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();20852086        <ItemListIndex>::insert(collection_id, current_index);20872088        // Update balance2089        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2090            .checked_add(1)2091            .unwrap();2092        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2093    }20942095    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2096        let current_index = <ItemListIndex>::get(item.collection)2097            .checked_add(1)2098            .unwrap();2099        let owner = item.owner.clone();21002101        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();21022103        <ItemListIndex>::insert(item.collection, current_index);21042105        // Update balance2106        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2107            .checked_add(item.value)2108            .unwrap();2109        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2110    }21112112    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2113        let current_index = <ItemListIndex>::get(item.collection)2114            .checked_add(1)2115            .unwrap();21162117        let value = item.owner.first().unwrap().fraction;2118        let owner = item.owner.first().unwrap().owner.clone();21192120        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();21212122        <ItemListIndex>::insert(item.collection, current_index);21232124        // Update balance2125        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2126            .checked_add(value)2127            .unwrap();2128        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2129    }21302131    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {21322133        // add to account limit2134        if <AccountItemCount<T>>::contains_key(owner.clone()) {21352136            // bound Owned tokens by a single address2137            let count = <AccountItemCount<T>>::get(owner.clone());2138            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21392140            <AccountItemCount<T>>::insert(owner.clone(), count2141                .checked_add(1)2142                .ok_or(Error::<T>::NumOverflow)?);2143        }2144        else {2145            <AccountItemCount<T>>::insert(owner.clone(), 1);2146        }21472148        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2149        if list_exists {2150            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2151            let item_contains = list.contains(&item_index.clone());21522153            if !item_contains {2154                list.push(item_index.clone());2155            }21562157            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2158        } else {2159            let mut itm = Vec::new();2160            itm.push(item_index.clone());2161            <AddressTokens<T>>::insert(collection_id, owner, itm);2162            2163        }21642165        Ok(())2166    }21672168    fn remove_token_index(2169        collection_id: CollectionId,2170        item_index: TokenId,2171        owner: T::AccountId,2172    ) -> DispatchResult {21732174        // update counter2175        <AccountItemCount<T>>::insert(owner.clone(), 2176            <AccountItemCount<T>>::get(owner.clone())2177            .checked_sub(1)2178            .ok_or(Error::<T>::NumOverflow)?);217921802181        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2182        if list_exists {2183            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2184            let item_contains = list.contains(&item_index.clone());21852186            if item_contains {2187                list.retain(|&item| item != item_index);2188                <AddressTokens<T>>::insert(collection_id, owner, list);2189            }2190        }21912192        Ok(())2193    }21942195    fn move_token_index(2196        collection_id: CollectionId,2197        item_index: TokenId,2198        old_owner: T::AccountId,2199        new_owner: T::AccountId,2200    ) -> DispatchResult {2201        Self::remove_token_index(collection_id, item_index, old_owner)?;2202        Self::add_token_index(collection_id, item_index, new_owner)?;22032204        Ok(())2205    }2206}22072208////////////////////////////////////////////////////////////////////////////////////////////////////2209// Economic models2210// #region22112212/// Fee multiplier.2213pub type Multiplier = FixedU128;22142215type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2216    <T as system::Trait>::AccountId,2217>>::Balance;2218type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2219    <T as system::Trait>::AccountId,2220>>::NegativeImbalance;22212222/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2223/// in the queue.2224#[derive(Encode, Decode, Clone, Eq, PartialEq)]2225pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2226    #[codec(compact)] BalanceOf<T>2227);22282229impl<T: Trait + Send + Sync> sp_std::fmt::Debug2230    for ChargeTransactionPayment<T>2231{2232    #[cfg(feature = "std")]2233    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2234        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2235    }2236    #[cfg(not(feature = "std"))]2237    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2238        Ok(())2239    }2240}22412242impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2243where2244    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2245    BalanceOf<T>: Send + Sync + FixedPointOperand,2246{2247    /// utility constructor. Used only in client/factory code.2248    pub fn from(fee: BalanceOf<T>) -> Self {2249        Self(fee)2250    }22512252    pub fn traditional_fee(2253        len: usize,2254        info: &DispatchInfoOf<T::Call>,2255        tip: BalanceOf<T>,2256    ) -> BalanceOf<T>2257    where2258        T::Call: Dispatchable<Info = DispatchInfo>,2259    {2260        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2261    }22622263    fn withdraw_fee(2264        &self,2265        who: &T::AccountId,2266        call: &T::Call,2267        info: &DispatchInfoOf<T::Call>,2268        len: usize,2269    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2270        let tip = self.0;22712272        // Set fee based on call type. Creating collection costs 1 Unique.2273        // All other transactions have traditional fees so far2274        // let fee = match call.is_sub_type() {2275        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2276        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2277        //                                                 // _ => <BalanceOf<T>>::from(100)2278        // };2279        let fee = Self::traditional_fee(len, info, tip);22802281        // Determine who is paying transaction fee based on ecnomic model2282        // Parse call to extract collection ID and access collection sponsor2283        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2284            Some(Call::create_item(collection_id, _owner, _properties)) => {22852286                // check free create limit2287                if <Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)2288                {2289                    <Collection<T>>::get(collection_id).sponsor2290                } else {2291                    T::AccountId::default()2292                }2293            }2294            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2295                2296                let _collection_limits = <Collection<T>>::get(collection_id).limits;2297                let _collection_mode = <Collection<T>>::get(collection_id).mode;22982299                // sponsor timeout2300                let sponsor_transfer = match _collection_mode {2301                    CollectionMode::NFT => {23022303                        // get correct limit2304                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2305                            _collection_limits.sponsor_transfer_timeout2306                        } else {2307                            ChainLimit::get().nft_sponsor_transfer_timeout2308                        };23092310                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2311                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2312                        let limit_time = basket + limit.into();2313                        if block_number >= limit_time {2314                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2315                            true2316                        }2317                        else {2318                            false2319                        }2320                    }2321                    CollectionMode::Fungible(_) => {23222323                        // get correct limit2324                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2325                            _collection_limits.sponsor_transfer_timeout2326                        } else {2327                            ChainLimit::get().fungible_sponsor_transfer_timeout2328                        };23292330                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2331                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2332                        if basket.iter().any(|i| i.address == _new_owner.clone())2333                        {2334                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2335                            let limit_time = item.start_block + limit.into();2336                            if block_number >= limit_time {2337                                basket.retain(|x| x.address == item.address);2338                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2339                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2340                                true2341                            }2342                            else {2343                                false2344                            }2345                        }2346                        else {2347                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2348                            true2349                        }2350                    }2351                    CollectionMode::ReFungible(_) => {23522353                        // get correct limit2354                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2355                            _collection_limits.sponsor_transfer_timeout2356                        } else {2357                            ChainLimit::get().refungible_sponsor_transfer_timeout2358                        };23592360                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2361                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2362                        let limit_time = basket + limit.into();2363                        if block_number >= limit_time {2364                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2365                            true2366                        } else {2367                            false2368                        }2369                    }2370                    _ => {2371                        false2372                    },2373                };23742375                if !sponsor_transfer {2376                    T::AccountId::default()2377                } else {2378                    <Collection<T>>::get(collection_id).sponsor2379                }2380            }23812382            _ => T::AccountId::default(),2383        };23842385        // Sponsor smart contracts2386        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {23872388            // On instantiation: set the contract owner2389            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {23902391                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2392                    code_hash,2393                    &data,2394                    &who,2395                );2396                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());23972398                T::AccountId::default()2399            },24002401            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2402            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {24032404                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());24052406                let mut sponsor_transfer = false;2407                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2408                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2409                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2410                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2411                    let limit_time = last_tx_block + rate_limit;24122413                    if block_number >= limit_time {2414                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2415                        sponsor_transfer = true;2416                    }2417                } else {2418                    sponsor_transfer = false;2419                }2420               2421                2422                let mut sp = T::AccountId::default();2423                if sponsor_transfer {2424                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2425                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2426                            sp = called_contract;2427                        }2428                    }2429                }24302431                sp2432            },24332434            _ => sponsor,2435        };24362437        let mut who_pays_fee: T::AccountId = sponsor.clone();2438        if sponsor == T::AccountId::default() {2439            who_pays_fee = who.clone();2440        }24412442        // Only mess with balances if fee is not zero.2443        if fee.is_zero() {2444            return Ok((fee, None));2445        }24462447        match <T as transaction_payment::Trait>::Currency::withdraw(2448            &who_pays_fee,2449            fee,2450            if tip.is_zero() {2451                WithdrawReason::TransactionPayment.into()2452            } else {2453                WithdrawReason::TransactionPayment | WithdrawReason::Tip2454            },2455            ExistenceRequirement::KeepAlive,2456        ) {2457            Ok(imbalance) => Ok((fee, Some(imbalance))),2458            Err(_) => Err(InvalidTransaction::Payment.into()),2459        }2460    }2461}246224632464impl<T: Trait + Send + Sync> SignedExtension2465    for ChargeTransactionPayment<T>2466where2467    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2468    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2469{2470    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2471    type AccountId = T::AccountId;2472    type Call = T::Call;2473    type AdditionalSigned = ();2474    type Pre = (2475        BalanceOf<T>,2476        Self::AccountId,2477        Option<NegativeImbalanceOf<T>>,2478        BalanceOf<T>,2479    );2480    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2481        Ok(())2482    }24832484    fn validate(2485        &self,2486        _who: &Self::AccountId,2487        _call: &Self::Call,2488        _info: &DispatchInfoOf<Self::Call>,2489        _len: usize,2490    ) -> TransactionValidity {2491        Ok(ValidTransaction::default())2492    }24932494    fn pre_dispatch(2495        self,2496        who: &Self::AccountId,2497        call: &Self::Call,2498        info: &DispatchInfoOf<Self::Call>,2499        len: usize,2500    ) -> Result<Self::Pre, TransactionValidityError> {2501        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2502        Ok((self.0, who.clone(), imbalance, fee))2503    }25042505    fn post_dispatch(2506        pre: Self::Pre,2507        info: &DispatchInfoOf<Self::Call>,2508        post_info: &PostDispatchInfoOf<Self::Call>,2509        len: usize,2510        _result: &DispatchResult,2511    ) -> Result<(), TransactionValidityError> {2512        let (tip, who, imbalance, fee) = pre;2513        if let Some(payed) = imbalance {2514            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2515                len as u32, info, post_info, tip,2516            );2517            let refund = fee.saturating_sub(actual_fee);2518            let actual_payment =2519                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2520                    &who, refund,2521                ) {2522                    Ok(refund_imbalance) => {2523                        // The refund cannot be larger than the up front payed max weight.2524                        // `PostDispatchInfo::calc_unspent` guards against such a case.2525                        match payed.offset(refund_imbalance) {2526                            Ok(actual_payment) => actual_payment,2527                            Err(_) => return Err(InvalidTransaction::Payment.into()),2528                        }2529                    }2530                    // We do not recreate the account using the refund. The up front payment2531                    // is gone in that case.2532                    Err(_) => payed,2533                };2534            let imbalances = actual_payment.split(tip);2535            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2536                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2537            );2538        }2539        Ok(())2540    }2541}25422543// #endregion
after · pallets/nft/src/lib.rs
1#![recursion_limit = "1024"]23#![cfg_attr(not(feature = "std"), no_std)]45#[cfg(feature = "std")]6pub use std::*;78#[cfg(feature = "std")]9pub use serde::*;1011use codec::{Decode, Encode};12pub use frame_support::{13    construct_runtime, decl_event, decl_module, decl_storage, decl_error,14    dispatch::DispatchResult,15    ensure, fail, parameter_types,16    traits::{17        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,18        Randomness, WithdrawReason,19    },20    weights::{21        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},22        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,23        WeightToFeePolynomial,24    },25    IsSubType, StorageValue,26};2728use frame_system::{self as system, ensure_signed, ensure_root};29use sp_runtime::sp_std::prelude::Vec;30use sp_runtime::{31    traits::{32        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SignedExtension, Zero,33    },34    transaction_validity::{35        InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,36    },37    FixedPointOperand, FixedU128,38};39use pallet_contracts::ContractAddressFor;40use sp_runtime::traits::StaticLookup;4142#[cfg(test)]43mod mock;4445#[cfg(test)]46mod tests;4748mod default_weights;4950pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;51pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;52pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;5354// Structs55// #region5657pub type CollectionId = u32;58pub type TokenId = u32;59pub type DecimalPoints = u8;6061#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]62#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]63pub enum CollectionMode {64    Invalid,65    NFT,66    // decimal points67    Fungible(DecimalPoints),68    // decimal points69    ReFungible(DecimalPoints),70}7172impl Into<u8> for CollectionMode {73    fn into(self) -> u8 {74        match self {75            CollectionMode::Invalid => 0,76            CollectionMode::NFT => 1,77            CollectionMode::Fungible(_) => 2,78            CollectionMode::ReFungible(_) => 3,79        }80    }81}8283#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]84#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]85pub enum AccessMode {86    Normal,87    WhiteList,88}89impl Default for AccessMode {90    fn default() -> Self {91        Self::Normal92    }93}9495impl Default for CollectionMode {96    fn default() -> Self {97        Self::Invalid98    }99}100101#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]102#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]103pub enum SchemaVersion {104    ImageURL,105    Unique,106}107impl Default for SchemaVersion {108    fn default() -> Self {109        Self::ImageURL110    }111}112113#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]114#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]115pub struct Ownership<AccountId> {116    pub owner: AccountId,117    pub fraction: u128,118}119120#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]121#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]122pub struct CollectionType<AccountId> {123    pub owner: AccountId,124    pub mode: CollectionMode,125    pub access: AccessMode,126    pub decimal_points: DecimalPoints,127    pub name: Vec<u16>,        // 64 include null escape char128    pub description: Vec<u16>, // 256 include null escape char129    pub token_prefix: Vec<u8>, // 16 include null escape char130    pub mint_mode: bool,131    pub offchain_schema: Vec<u8>,132    pub schema_version: SchemaVersion,133    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender134    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship135    pub limits: CollectionLimits, // Collection private restrictions 136    pub variable_on_chain_schema: Vec<u8>, //137    pub const_on_chain_schema: Vec<u8>, //138}139140#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]141#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]142pub struct NftItemType<AccountId> {143    pub collection: CollectionId,144    pub owner: AccountId,145    pub const_data: Vec<u8>,146    pub variable_data: Vec<u8>,147}148149#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]150#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]151pub struct FungibleItemType<AccountId> {152    pub collection: CollectionId,153    pub owner: AccountId,154    pub value: u128,155}156157#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]158#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]159pub struct ReFungibleItemType<AccountId> {160    pub collection: CollectionId,161    pub owner: Vec<Ownership<AccountId>>,162    pub const_data: Vec<u8>,163    pub variable_data: Vec<u8>,164}165166#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]167#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]168pub struct ApprovePermissions<AccountId> {169    pub approved: AccountId,170    pub amount: u128,171}172173#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]174#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]175pub struct VestingItem<AccountId, Moment> {176    pub sender: AccountId,177    pub recipient: AccountId,178    pub collection_id: CollectionId,179    pub item_id: TokenId,180    pub amount: u64,181    pub vesting_date: Moment,182}183184#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]185#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]186pub struct BasketItem<AccountId, BlockNumber> {187    pub address: AccountId,188    pub start_block: BlockNumber,189}190191#[derive(Encode, Decode, Debug, Clone, PartialEq)]192#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]193pub struct CollectionLimits {194    pub account_token_ownership_limit: u32,195    pub sponsored_data_size: u32,196    pub token_limit: u32,197198    // Timeouts for item types in passed blocks199    pub sponsor_transfer_timeout: u32,200}201202impl Default for CollectionLimits {203    fn default() -> CollectionLimits {204        CollectionLimits { 205            account_token_ownership_limit: 10_000_000, 206            token_limit: u32::max_value(),207            sponsored_data_size: u32::max_value(), 208            sponsor_transfer_timeout: 14400 }209    }210}211212#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]213#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]214pub struct ChainLimits {215    pub collection_numbers_limit: u32,216    pub account_token_ownership_limit: u32,217    pub collections_admins_limit: u64,218    pub custom_data_limit: u32,219220    // Timeouts for item types in passed blocks221    pub nft_sponsor_transfer_timeout: u32,222    pub fungible_sponsor_transfer_timeout: u32,223    pub refungible_sponsor_transfer_timeout: u32,224}225226pub trait WeightInfo {227	fn create_collection() -> Weight;228	fn destroy_collection() -> Weight;229	fn add_to_white_list() -> Weight;230	fn remove_from_white_list() -> Weight;231    fn set_public_access_mode() -> Weight;232    fn set_mint_permission() -> Weight;233    fn change_collection_owner() -> Weight;234    fn add_collection_admin() -> Weight;235    fn remove_collection_admin() -> Weight;236    fn set_collection_sponsor() -> Weight;237    fn confirm_sponsorship() -> Weight;238    fn remove_collection_sponsor() -> Weight;239    fn create_item(s: usize) -> Weight;240    fn burn_item() -> Weight;241    fn transfer() -> Weight;242    fn approve() -> Weight;243    fn transfer_from() -> Weight;244    fn set_offchain_schema() -> Weight;245    fn set_const_on_chain_schema() -> Weight;246    fn set_variable_on_chain_schema() -> Weight;247    fn set_variable_meta_data() -> Weight;248    fn enable_contract_sponsoring() -> Weight;249}250251#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]252#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]253pub struct CreateNftData {254    pub const_data: Vec<u8>,255    pub variable_data: Vec<u8>,256}257258#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]259#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]260pub struct CreateFungibleData {261}262263#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]264#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]265pub struct CreateReFungibleData {266    pub const_data: Vec<u8>,267    pub variable_data: Vec<u8>,268}269270#[derive(Encode, Decode, Debug, Clone, PartialEq)]271#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]272pub enum CreateItemData {273    NFT(CreateNftData),274    Fungible(CreateFungibleData),275    ReFungible(CreateReFungibleData),276}277278impl CreateItemData {279    pub fn len(&self) -> usize {280        let len = match self {281            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),282            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),283            _ => 0284        };285        286        return len;287    }288}289290impl From<CreateNftData> for CreateItemData {291    fn from(item: CreateNftData) -> Self {292        CreateItemData::NFT(item)293    }294}295296impl From<CreateReFungibleData> for CreateItemData {297    fn from(item: CreateReFungibleData) -> Self {298        CreateItemData::ReFungible(item)299    }300}301302impl From<CreateFungibleData> for CreateItemData {303    fn from(item: CreateFungibleData) -> Self {304        CreateItemData::Fungible(item)305    }306}307308309decl_error! {310	/// Error for non-fungible-token module.311	pub enum Error for Module<T: Trait> {312        /// Total collections bound exceeded.313        TotalCollectionsLimitExceeded,314		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.315        CollectionDecimalPointLimitExceeded, 316        /// Collection name can not be longer than 63 char.317        CollectionNameLimitExceeded, 318        /// Collection description can not be longer than 255 char.319        CollectionDescriptionLimitExceeded, 320        /// Token prefix can not be longer than 15 char.321        CollectionTokenPrefixLimitExceeded,322        /// This collection does not exist.323        CollectionNotFound,324        /// Item not exists.325        TokenNotFound,326        /// Arithmetic calculation overflow.327        NumOverflow,       328        /// Account already has admin role.329        AlreadyAdmin,  330        /// You do not own this collection.331        NoPermission,332        /// This address is not set as sponsor, use setCollectionSponsor first.333        ConfirmUnsetSponsorFail,334        /// Collection is not in mint mode.335        PublicMintingNotAllowed,336        /// Sender parameter and item owner must be equal.337        MustBeTokenOwner,338        /// Item balance not enough.339        TokenValueTooLow,340        /// Size of item is too large.341        NftSizeLimitExceeded,342        /// No approve found343        ApproveNotFound,344        /// Requested value more than approved.345        TokenValueNotEnough,346        /// Only approved addresses can call this method.347        ApproveRequired,348        /// Address is not in white list.349        AddresNotInWhiteList,350        /// Number of collection admins bound exceeded.351        CollectionAdminsLimitExceeded,352        /// Owned tokens by a single address bound exceeded.353        AddressOwnershipLimitExceeded,354        /// Length of items properties must be greater than 0.355        EmptyArgument,356        /// const_data exceeded data limit.357        TokenConstDataLimitExceeded,358        /// variable_data exceeded data limit.359        TokenVariableDataLimitExceeded,360        /// Not NFT item data used to mint in NFT collection.361        NotNftDataUsedToMintNftCollectionToken,362        /// Not Fungible item data used to mint in Fungible collection.363        NotFungibleDataUsedToMintFungibleCollectionToken,364        /// Not Re Fungible item data used to mint in Re Fungible collection.365        NotReFungibleDataUsedToMintReFungibleCollectionToken,366        /// Unexpected collection type.367        UnexpectedCollectionType,368        /// Can't store metadata in fungible tokens.369        CantStoreMetadataInFungibleTokens,370        /// Collection token limit exceeded371        CollectionTokenLimitExceeded,372        /// Account token limit exceeded per collection373        AccountTokenLimitExceeded,374        /// Collection limit bounds per collection exceeded375        CollectionLimitBoundsExceeded376	}377}378379pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {380    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;381382    /// Weight information for extrinsics in this pallet.383	type WeightInfo: WeightInfo;384}385386#[cfg(feature = "runtime-benchmarks")]387mod benchmarking;388389// #endregion390391decl_storage! {392    trait Store for Module<T: Trait> as Nft {393394        // Private members395        NextCollectionID: CollectionId;396        CreatedCollectionCount: u32;397        ChainVersion: u64;398        ItemListIndex: map hasher(identity) CollectionId => TokenId;399400        // Chain limits struct401        pub ChainLimit get(fn chain_limit) config(): ChainLimits;402403        // Bound counters404        CollectionCount: u32;405        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;406407        // Basic collections408        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;409        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;410        pub WhiteList get(fn white_list): map hasher(identity) CollectionId => Vec<T::AccountId>;411412        /// Balance owner per collection map413        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;414415        /// second parameter: item id + owner account id416        pub ApprovedList get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;417418        /// Item collections419        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;420        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => FungibleItemType<T::AccountId>;421        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;422423        /// Index list424        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;425426        /// Tokens transfer baskets427        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;428        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Vec<BasketItem<T::AccountId, T::BlockNumber>>;429        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;430431        // Contract Sponsorship and Ownership432        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;433        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;434        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;435        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;436    }437    add_extra_genesis {438        build(|config: &GenesisConfig<T>| {439            // Modification of storage440            for (_num, _c) in &config.collection {441                <Module<T>>::init_collection(_c);442            }443444            for (_num, _q, _i) in &config.nft_item_id {445                <Module<T>>::init_nft_token(_i);446            }447448            for (_num, _q, _i) in &config.fungible_item_id {449                <Module<T>>::init_fungible_token(_i);450            }451452            for (_num, _q, _i) in &config.refungible_item_id {453                <Module<T>>::init_refungible_token(_i);454            }455        })456    }457}458459decl_event!(460    pub enum Event<T>461    where462        AccountId = <T as system::Trait>::AccountId,463    {464        /// New collection was created465        /// 466        /// # Arguments467        /// 468        /// * collection_id: Globally unique identifier of newly created collection.469        /// 470        /// * mode: [CollectionMode] converted into u8.471        /// 472        /// * account_id: Collection owner.473        Created(CollectionId, u8, AccountId),474475        /// New item was created.476        /// 477        /// # Arguments478        /// 479        /// * collection_id: Id of the collection where item was created.480        /// 481        /// * item_id: Id of an item. Unique within the collection.482        ItemCreated(CollectionId, TokenId),483484        /// Collection item was burned.485        /// 486        /// # Arguments487        /// 488        /// collection_id.489        /// 490        /// item_id: Identifier of burned NFT.491        ItemDestroyed(CollectionId, TokenId),492    }493);494495decl_module! {496    pub struct Module<T: Trait> for enum Call where origin: T::Origin {497498        fn deposit_event() = default;499        type Error = Error<T>;500501        fn on_initialize(now: T::BlockNumber) -> Weight {502503            if ChainVersion::get() < 2504            {505                let value = NextCollectionID::get();506                CreatedCollectionCount::put(value);507                ChainVersion::put(2);508            }509510            0511        }512513        /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.514        /// 515        /// # Permissions516        /// 517        /// * Anyone.518        /// 519        /// # Arguments520        /// 521        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.522        /// 523        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.524        /// 525        /// * token_prefix: UTF-8 string with token prefix.526        /// 527        /// * mode: [CollectionMode] collection type and type dependent data.528        // returns collection ID529        #[weight = T::WeightInfo::create_collection()]530        pub fn create_collection(origin,531                                 collection_name: Vec<u16>,532                                 collection_description: Vec<u16>,533                                 token_prefix: Vec<u8>,534                                 mode: CollectionMode) -> DispatchResult {535536            // Anyone can create a collection537            let who = ensure_signed(origin)?;538539            let decimal_points = match mode {540                CollectionMode::Fungible(points) => points,541                CollectionMode::ReFungible(points) => points,542                _ => 0543            };544545            // bound Total number of collections546            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);547548            // check params549            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);550551            let mut name = collection_name.to_vec();552            name.push(0);553            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);554555            let mut description = collection_description.to_vec();556            description.push(0);557            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);558559            let mut prefix = token_prefix.to_vec();560            prefix.push(0);561            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);562563            // Generate next collection ID564            let next_id = CreatedCollectionCount::get()565                .checked_add(1)566                .ok_or(Error::<T>::NumOverflow)?;567568            // bound counter569            let total = CollectionCount::get()570                .checked_add(1)571                .ok_or(Error::<T>::NumOverflow)?;572573            CreatedCollectionCount::put(next_id);574            CollectionCount::put(total);575576            // Create new collection577            let new_collection = CollectionType {578                owner: who.clone(),579                name: name,580                mode: mode.clone(),581                mint_mode: false,582                access: AccessMode::Normal,583                description: description,584                decimal_points: decimal_points,585                token_prefix: prefix,586                offchain_schema: Vec::new(),587                schema_version: SchemaVersion::ImageURL,588                sponsor: T::AccountId::default(),589                unconfirmed_sponsor: T::AccountId::default(),590                variable_on_chain_schema: Vec::new(),591                const_on_chain_schema: Vec::new(),592                limits: CollectionLimits::default(),593            };594595            // Add new collection to map596            <Collection<T>>::insert(next_id, new_collection);597598            // call event599            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));600601            Ok(())602        }603604        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.605        /// 606        /// # Permissions607        /// 608        /// * Collection Owner.609        /// 610        /// # Arguments611        /// 612        /// * collection_id: collection to destroy.613        #[weight = T::WeightInfo::destroy_collection()]614        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {615616            let sender = ensure_signed(origin)?;617            Self::check_owner_permissions(collection_id, sender)?;618619            <AddressTokens<T>>::remove_prefix(collection_id);620            <ApprovedList<T>>::remove_prefix(collection_id);621            <Balance<T>>::remove_prefix(collection_id);622            <ItemListIndex>::remove(collection_id);623            <AdminList<T>>::remove(collection_id);624            <Collection<T>>::remove(collection_id);625            <WhiteList<T>>::remove(collection_id);626627            <NftItemList<T>>::remove_prefix(collection_id);628            <FungibleItemList<T>>::remove_prefix(collection_id);629            <ReFungibleItemList<T>>::remove_prefix(collection_id);630631            <NftTransferBasket<T>>::remove_prefix(collection_id);632            <FungibleTransferBasket<T>>::remove_prefix(collection_id);633            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);634635            if CollectionCount::get() > 0636            {637                // bound couter638                let total = CollectionCount::get()639                    .checked_sub(1)640                    .ok_or(Error::<T>::NumOverflow)?;641642                CollectionCount::put(total);643            }644645            Ok(())646        }647648        /// Add an address to white list.649        /// 650        /// # Permissions651        /// 652        /// * Collection Owner653        /// * Collection Admin654        /// 655        /// # Arguments656        /// 657        /// * collection_id.658        /// 659        /// * address.660        #[weight = T::WeightInfo::add_to_white_list()]661        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{662663            let sender = ensure_signed(origin)?;664            Self::check_owner_or_admin_permissions(collection_id, sender)?;665666            let mut white_list_collection: Vec<T::AccountId>;667            if <WhiteList<T>>::contains_key(collection_id) {668                white_list_collection = <WhiteList<T>>::get(collection_id);669                if !white_list_collection.contains(&address.clone())670                {671                    white_list_collection.push(address.clone());672                }673            }674            else {675                white_list_collection = Vec::new();676                white_list_collection.push(address.clone());677            }678679            <WhiteList<T>>::insert(collection_id, white_list_collection);680            Ok(())681        }682683        /// Remove an address from white list.684        /// 685        /// # Permissions686        /// 687        /// * Collection Owner688        /// * Collection Admin689        /// 690        /// # Arguments691        /// 692        /// * collection_id.693        /// 694        /// * address.695        #[weight = T::WeightInfo::remove_from_white_list()]696        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{697698            let sender = ensure_signed(origin)?;699            Self::check_owner_or_admin_permissions(collection_id, sender)?;700701            if <WhiteList<T>>::contains_key(collection_id) {702                let mut white_list_collection = <WhiteList<T>>::get(collection_id);703                if white_list_collection.contains(&address.clone())704                {705                    white_list_collection.retain(|i| *i != address.clone());706                    <WhiteList<T>>::insert(collection_id, white_list_collection);707                }708            }709710            Ok(())711        }712713        /// Toggle between normal and white list access for the methods with access for `Anyone`.714        /// 715        /// # Permissions716        /// 717        /// * Collection Owner.718        /// 719        /// # Arguments720        /// 721        /// * collection_id.722        /// 723        /// * mode: [AccessMode]724        #[weight = T::WeightInfo::set_public_access_mode()]725        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult726        {727            let sender = ensure_signed(origin)?;728729            Self::check_owner_permissions(collection_id, sender)?;730            let mut target_collection = <Collection<T>>::get(collection_id);731            target_collection.access = mode;732            <Collection<T>>::insert(collection_id, target_collection);733734            Ok(())735        }736737        /// Allows Anyone to create tokens if:738        /// * White List is enabled, and739        /// * Address is added to white list, and740        /// * This method was called with True parameter741        /// 742        /// # Permissions743        /// * Collection Owner744        ///745        /// # Arguments746        /// 747        /// * collection_id.748        /// 749        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.750        #[weight = T::WeightInfo::set_mint_permission()]751        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult752        {753            let sender = ensure_signed(origin)?;754755            Self::check_owner_permissions(collection_id, sender)?;756            let mut target_collection = <Collection<T>>::get(collection_id);757            target_collection.mint_mode = mint_permission;758            <Collection<T>>::insert(collection_id, target_collection);759760            Ok(())761        }762763        /// Change the owner of the collection.764        /// 765        /// # Permissions766        /// 767        /// * Collection Owner.768        /// 769        /// # Arguments770        /// 771        /// * collection_id.772        /// 773        /// * new_owner.774        #[weight = T::WeightInfo::change_collection_owner()]775        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {776777            let sender = ensure_signed(origin)?;778            Self::check_owner_permissions(collection_id, sender)?;779            let mut target_collection = <Collection<T>>::get(collection_id);780            target_collection.owner = new_owner;781            <Collection<T>>::insert(collection_id, target_collection);782783            Ok(())784        }785786        /// Adds an admin of the Collection.787        /// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership. 788        /// 789        /// # Permissions790        /// 791        /// * Collection Owner.792        /// * Collection Admin.793        /// 794        /// # Arguments795        /// 796        /// * collection_id: ID of the Collection to add admin for.797        /// 798        /// * new_admin_id: Address of new admin to add.799        #[weight = T::WeightInfo::add_collection_admin()]800        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {801802            let sender = ensure_signed(origin)?;803            Self::check_owner_or_admin_permissions(collection_id, sender)?;804            let mut admin_arr: Vec<T::AccountId> = Vec::new();805806            if <AdminList<T>>::contains_key(collection_id)807            {808                admin_arr = <AdminList<T>>::get(collection_id);809                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);810            }811812            // Number of collection admins813            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);814815            admin_arr.push(new_admin_id);816            <AdminList<T>>::insert(collection_id, admin_arr);817818            Ok(())819        }820821        /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.822        ///823        /// # Permissions824        /// 825        /// * Collection Owner.826        /// * Collection Admin.827        /// 828        /// # Arguments829        /// 830        /// * collection_id: ID of the Collection to remove admin for.831        /// 832        /// * account_id: Address of admin to remove.833        #[weight = T::WeightInfo::remove_collection_admin()]834        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {835836            let sender = ensure_signed(origin)?;837            Self::check_owner_or_admin_permissions(collection_id, sender)?;838839            if <AdminList<T>>::contains_key(collection_id)840            {841                let mut admin_arr = <AdminList<T>>::get(collection_id);842                admin_arr.retain(|i| *i != account_id);843                <AdminList<T>>::insert(collection_id, admin_arr);844            }845846            Ok(())847        }848849        /// # Permissions850        /// 851        /// * Collection Owner852        /// 853        /// # Arguments854        /// 855        /// * collection_id.856        /// 857        /// * new_sponsor.858        #[weight = T::WeightInfo::set_collection_sponsor()]859        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {860861            let sender = ensure_signed(origin)?;862            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);863864            let mut target_collection = <Collection<T>>::get(collection_id);865            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);866867            target_collection.unconfirmed_sponsor = new_sponsor;868            <Collection<T>>::insert(collection_id, target_collection);869870            Ok(())871        }872873        /// # Permissions874        /// 875        /// * Sponsor.876        /// 877        /// # Arguments878        /// 879        /// * collection_id.880        #[weight = T::WeightInfo::confirm_sponsorship()]881        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {882883            let sender = ensure_signed(origin)?;884            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);885886            let mut target_collection = <Collection<T>>::get(collection_id);887            ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);888889            target_collection.sponsor = target_collection.unconfirmed_sponsor;890            target_collection.unconfirmed_sponsor = T::AccountId::default();891            <Collection<T>>::insert(collection_id, target_collection);892893            Ok(())894        }895896        /// Switch back to pay-per-own-transaction model.897        ///898        /// # Permissions899        ///900        /// * Collection owner.901        /// 902        /// # Arguments903        /// 904        /// * collection_id.905        #[weight = T::WeightInfo::remove_collection_sponsor()]906        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {907908            let sender = ensure_signed(origin)?;909            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);910911            let mut target_collection = <Collection<T>>::get(collection_id);912            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);913914            target_collection.sponsor = T::AccountId::default();915            <Collection<T>>::insert(collection_id, target_collection);916917            Ok(())918        }919920        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.921        /// 922        /// # Permissions923        /// 924        /// * Collection Owner.925        /// * Collection Admin.926        /// * Anyone if927        ///     * White List is enabled, and928        ///     * Address is added to white list, and929        ///     * MintPermission is enabled (see SetMintPermission method)930        /// 931        /// # Arguments932        /// 933        /// * collection_id: ID of the collection.934        /// 935        /// * owner: Address, initial owner of the NFT.936        ///937        /// * data: Token data to store on chain.938        // #[weight =939        // (130_000_000 as Weight)940        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))941        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))942        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]943944        #[weight = T::WeightInfo::create_item(data.len())]945        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {946947            let sender = ensure_signed(origin)?;948949            Self::collection_exists(collection_id)?;950951            let target_collection = <Collection<T>>::get(collection_id);952953            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;954            Self::validate_create_item_args(&target_collection, &data)?;955            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;956957            Ok(())958        }959960        /// This method creates multiple instances of NFT Collection created with CreateCollection method.961        /// 962        /// # Permissions963        /// 964        /// * Collection Owner.965        /// * Collection Admin.966        /// * Anyone if967        ///     * White List is enabled, and968        ///     * Address is added to white list, and969        ///     * MintPermission is enabled (see SetMintPermission method)970        /// 971        /// # Arguments972        /// 973        /// * collection_id: ID of the collection.974        /// 975        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].976        /// 977        /// * owner: Address, initial owner of the NFT.978        #[weight = T::WeightInfo::create_item(items_data.into_iter()979                               .map(|data| { data.len() })980                               .sum())]981        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {982983            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);984            let sender = ensure_signed(origin)?;985986            Self::collection_exists(collection_id)?;987            let target_collection = <Collection<T>>::get(collection_id);988989            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;990991            for data in &items_data {992                Self::validate_create_item_args(&target_collection, data)?;993            }994            for data in &items_data {995                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;996            }997998            Ok(())999        }10001001        /// Destroys a concrete instance of NFT.1002        /// 1003        /// # Permissions1004        /// 1005        /// * Collection Owner.1006        /// * Collection Admin.1007        /// * Current NFT Owner.1008        /// 1009        /// # Arguments1010        /// 1011        /// * collection_id: ID of the collection.1012        /// 1013        /// * item_id: ID of NFT to burn.1014        #[weight = T::WeightInfo::burn_item()]1015        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {10161017            let sender = ensure_signed(origin)?;1018            Self::collection_exists(collection_id)?;10191020            // Transfer permissions check1021            let target_collection = <Collection<T>>::get(collection_id);1022            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1023                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1024                Error::<T>::NoPermission);10251026            if target_collection.access == AccessMode::WhiteList {1027                Self::check_white_list(collection_id, &sender)?;1028            }10291030            match target_collection.mode1031            {1032                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1033                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,1034                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1035                _ => ()1036            };10371038            // call event1039            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10401041            Ok(())1042        }10431044        /// Change ownership of the token.1045        /// 1046        /// # Permissions1047        /// 1048        /// * Collection Owner1049        /// * Collection Admin1050        /// * Current NFT owner1051        ///1052        /// # Arguments1053        /// 1054        /// * recipient: Address of token recipient.1055        /// 1056        /// * collection_id.1057        /// 1058        /// * item_id: ID of the item1059        ///     * Non-Fungible Mode: Required.1060        ///     * Fungible Mode: Ignored.1061        ///     * Re-Fungible Mode: Required.1062        /// 1063        /// * value: Amount to transfer.1064        ///     * Non-Fungible Mode: Ignored1065        ///     * Fungible Mode: Must specify transferred amount1066        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1067        #[weight = T::WeightInfo::transfer()]1068        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10691070            let sender = ensure_signed(origin)?;1071            let target_collection = <Collection<T>>::get(collection_id);10721073            // Limits check1074            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;10751076            // Transfer permissions check1077            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1078                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1079                Error::<T>::NoPermission);10801081            if target_collection.access == AccessMode::WhiteList {1082                Self::check_white_list(collection_id, &sender)?;1083                Self::check_white_list(collection_id, &recipient)?;1084            }10851086            match target_collection.mode1087            {1088                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1089                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1090                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1091                _ => ()1092            };10931094            Ok(())1095        }10961097        /// Set, change, or remove approved address to transfer the ownership of the NFT.1098        /// 1099        /// # Permissions1100        /// 1101        /// * Collection Owner1102        /// * Collection Admin1103        /// * Current NFT owner1104        /// 1105        /// # Arguments1106        /// 1107        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1108        /// 1109        /// * collection_id.1110        /// 1111        /// * item_id: ID of the item.1112        #[weight = T::WeightInfo::approve()]1113        pub fn approve(origin, approved: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {11141115            let sender = ensure_signed(origin)?;11161117            // Transfer permissions check1118            let target_collection = <Collection<T>>::get(collection_id);1119            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1120                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1121                Error::<T>::NoPermission);11221123            if target_collection.access == AccessMode::WhiteList {1124                Self::check_white_list(collection_id, &sender)?;1125                Self::check_white_list(collection_id, &approved)?;1126            }11271128            // amount param stub1129            let amount = 100000000;11301131            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1132            if list_exists {11331134                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1135                let item_contains = list.iter().any(|i| i.approved == approved);11361137                if !item_contains {1138                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1139                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1140                }1141            } else {11421143                let mut list = Vec::new();1144                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1145                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1146            }11471148            Ok(())1149        }1150        1151        /// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.1152        /// 1153        /// # Permissions1154        /// * Collection Owner1155        /// * Collection Admin1156        /// * Current NFT owner1157        /// * Address approved by current NFT owner1158        /// 1159        /// # Arguments1160        /// 1161        /// * from: Address that owns token.1162        /// 1163        /// * recipient: Address of token recipient.1164        /// 1165        /// * collection_id.1166        /// 1167        /// * item_id: ID of the item.1168        /// 1169        /// * value: Amount to transfer.1170        #[weight = T::WeightInfo::transfer_from()]1171        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11721173            let sender = ensure_signed(origin)?;1174            let mut appoved_transfer = false;11751176            // Check approve1177            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1178                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1179                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1180                if opt_item.is_some()1181                {1182                    appoved_transfer = true;1183                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1184                }1185            }11861187            let target_collection = <Collection<T>>::get(collection_id);11881189            // Limits check1190            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;11911192            // Transfer permissions check         1193            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1194            Error::<T>::NoPermission);11951196            if target_collection.access == AccessMode::WhiteList {1197                Self::check_white_list(collection_id, &sender)?;1198                Self::check_white_list(collection_id, &recipient)?;1199            }12001201            // remove approve1202            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1203                .into_iter().filter(|i| i.approved != sender.clone()).collect();1204            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);120512061207            match target_collection.mode1208            {1209                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1210                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1211                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1212                _ => ()1213            };12141215            Ok(())1216        }12171218        #[weight = 0]1219        pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {12201221            // let no_perm_mes = "You do not have permissions to modify this collection";1222            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1223            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1224            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);12251226            // // on_nft_received  call12271228            // Self::transfer(origin, collection_id, item_id, new_owner)?;12291230            Ok(())1231        }12321233        /// Set off-chain data schema.1234        /// 1235        /// # Permissions1236        /// 1237        /// * Collection Owner1238        /// * Collection Admin1239        /// 1240        /// # Arguments1241        /// 1242        /// * collection_id.1243        /// 1244        /// * schema: String representing the offchain data schema.1245        #[weight = T::WeightInfo::set_variable_meta_data()]1246        pub fn set_variable_meta_data (1247            origin,1248            collection_id: CollectionId,1249            item_id: TokenId,1250            data: Vec<u8>1251        ) -> DispatchResult {1252            let sender = ensure_signed(origin)?;1253            1254            Self::collection_exists(collection_id)?;1255            1256            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12571258            // Modify permissions check1259            let target_collection = <Collection<T>>::get(collection_id);1260            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1261                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1262                Error::<T>::NoPermission);12631264            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12651266            match target_collection.mode1267            {1268                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1269                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1270                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1271                _ => fail!(Error::<T>::UnexpectedCollectionType)1272            };12731274            Ok(())1275        }1276 1277        /// Set schema standard1278        /// ImageURL1279        /// Unique1280        /// 1281        /// # Permissions1282        /// 1283        /// * Collection Owner1284        /// * Collection Admin1285        /// 1286        /// # Arguments1287        /// 1288        /// * collection_id.1289        /// 1290        /// * schema: SchemaVersion: enum1291        #[weight = 0]1292        pub fn set_schema_version(1293            origin,1294            collection_id: CollectionId,1295            version: SchemaVersion1296        ) -> DispatchResult {1297            let sender = ensure_signed(origin)?;1298            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1299            let mut target_collection = <Collection<T>>::get(collection_id);1300            target_collection.schema_version = version;1301            <Collection<T>>::insert(collection_id, target_collection);13021303            Ok(())1304        }13051306        /// Set off-chain data schema.1307        /// 1308        /// # Permissions1309        /// 1310        /// * Collection Owner1311        /// * Collection Admin1312        /// 1313        /// # Arguments1314        /// 1315        /// * collection_id.1316        /// 1317        /// * schema: String representing the offchain data schema.1318        #[weight = T::WeightInfo::set_offchain_schema()]1319        pub fn set_offchain_schema(1320            origin,1321            collection_id: CollectionId,1322            schema: Vec<u8>1323        ) -> DispatchResult {1324            let sender = ensure_signed(origin)?;1325            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13261327            let mut target_collection = <Collection<T>>::get(collection_id);1328            target_collection.offchain_schema = schema;1329            <Collection<T>>::insert(collection_id, target_collection);13301331            Ok(())1332        }13331334        /// Set const on-chain data schema.1335        /// 1336        /// # Permissions1337        /// 1338        /// * Collection Owner1339        /// * Collection Admin1340        /// 1341        /// # Arguments1342        /// 1343        /// * collection_id.1344        /// 1345        /// * schema: String representing the const on-chain data schema.1346        #[weight = T::WeightInfo::set_const_on_chain_schema()]1347        pub fn set_const_on_chain_schema (1348            origin,1349            collection_id: CollectionId,1350            schema: Vec<u8>1351        ) -> DispatchResult {1352            let sender = ensure_signed(origin)?;1353            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13541355            let mut target_collection = <Collection<T>>::get(collection_id);1356            target_collection.const_on_chain_schema = schema;1357            <Collection<T>>::insert(collection_id, target_collection);13581359            Ok(())1360        }13611362        /// Set variable on-chain data schema.1363        /// 1364        /// # Permissions1365        /// 1366        /// * Collection Owner1367        /// * Collection Admin1368        /// 1369        /// # Arguments1370        /// 1371        /// * collection_id.1372        /// 1373        /// * schema: String representing the variable on-chain data schema.1374        #[weight = T::WeightInfo::set_const_on_chain_schema()]1375        pub fn set_variable_on_chain_schema (1376            origin,1377            collection_id: CollectionId,1378            schema: Vec<u8>1379        ) -> DispatchResult {1380            let sender = ensure_signed(origin)?;1381            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13821383            let mut target_collection = <Collection<T>>::get(collection_id);1384            target_collection.variable_on_chain_schema = schema;1385            <Collection<T>>::insert(collection_id, target_collection);13861387            Ok(())1388        }13891390        // Sudo permissions function1391        #[weight = 0]1392        pub fn set_chain_limits(1393            origin,1394            limits: ChainLimits1395        ) -> DispatchResult {1396            ensure_root(origin)?;1397            <ChainLimit>::put(limits);1398            Ok(())1399        }14001401        /// Enable smart contract self-sponsoring.1402        /// 1403        /// # Permissions1404        /// 1405        /// * Contract Owner1406        /// 1407        /// # Arguments1408        /// 1409        /// * contract address1410        /// * enable flag1411        /// 1412        #[weight = T::WeightInfo::enable_contract_sponsoring()]1413        pub fn enable_contract_sponsoring(1414            origin,1415            contract_address: T::AccountId,1416            enable: bool1417        ) -> DispatchResult {14181419            let sender = ensure_signed(origin)?;14201421            #[cfg(feature = "runtime-benchmarks")]1422            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14231424            let mut is_owner = false;1425            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1426                let owner = <ContractOwner<T>>::get(&contract_address);1427                is_owner = sender == owner;1428            }1429            ensure!(is_owner, Error::<T>::NoPermission);14301431            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1432            Ok(())1433        }14341435        /// Set the rate limit for contract sponsoring to specified number of blocks.1436        /// 1437        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1438        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1439        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1440        /// from contract endowment if there are at least B blocks between such transactions. 1441        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1442        /// 1443        /// # Permissions1444        /// 1445        /// * Contract Owner1446        /// 1447        /// # Arguments1448        /// 1449        /// -`contract_address`: Address of the contract to sponsor1450        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1451        /// 1452        #[weight = 0]1453        pub fn set_contract_sponsoring_rate_limit(1454            origin,1455            contract_address: T::AccountId,1456            rate_limit: T::BlockNumber1457        ) -> DispatchResult {1458            let sender = ensure_signed(origin)?;1459            let mut is_owner = false;1460            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1461                let owner = <ContractOwner<T>>::get(&contract_address);1462                is_owner = sender == owner;1463            }1464            ensure!(is_owner, Error::<T>::NoPermission);14651466            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1467            Ok(())1468        }14691470        #[weight = 0]1471        pub fn set_collection_limits(1472            origin,1473            collection_id: u32,1474            limits: CollectionLimits,1475        ) -> DispatchResult {1476            let sender = ensure_signed(origin)?;1477            Self::check_owner_permissions(collection_id, sender.clone())?;1478            let mut target_collection = <Collection<T>>::get(collection_id);1479            let chain_limits = ChainLimit::get();1480            let climits = target_collection.limits;14811482            // collection bounds1483            ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1484                limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP,  1485                Error::<T>::CollectionLimitBoundsExceeded);14861487            // token_limit   check  prev1488            ensure!(climits.token_limit > limits.token_limit && 1489                limits.token_limit <= chain_limits.account_token_ownership_limit, 1490                Error::<T>::AccountTokenLimitExceeded);14911492            target_collection.limits = limits;1493            <Collection<T>>::insert(collection_id, target_collection);14941495            Ok(())1496        } 1497    }1498}14991500impl<T: Trait> Module<T> {15011502    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {15031504        // check token limit and account token limit1505        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1506        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1507        1508        Ok(())1509    }15101511    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {15121513        // check token limit and account token limit1514        let total_items: u32 = ItemListIndex::get(collection_id);1515        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1516        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1517        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);15181519        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1520            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1521            Self::check_white_list(collection_id, owner)?;1522            Self::check_white_list(collection_id, sender)?;1523        }15241525        Ok(())1526    }15271528    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1529        match target_collection.mode1530        {1531            CollectionMode::NFT => {1532                if let CreateItemData::NFT(data) = data {1533                    // check sizes1534                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1535                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1536                } else {1537                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1538                }1539            },1540            CollectionMode::Fungible(_) => {1541                if let CreateItemData::Fungible(_) = data {1542                } else {1543                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1544                }1545            },1546            CollectionMode::ReFungible(_) => {1547                if let CreateItemData::ReFungible(data) = data {15481549                    // check sizes1550                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1551                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1552                } else {1553                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1554                }1555            },1556            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1557        };15581559        Ok(())1560    }15611562    fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1563        match data1564        {1565            CreateItemData::NFT(data) => {1566                let item = NftItemType {1567                    collection: collection_id,1568                    owner,1569                    const_data: data.const_data,1570                    variable_data: data.variable_data1571                };15721573                Self::add_nft_item(item)?;1574            },1575            CreateItemData::Fungible(_) => {1576                let item = FungibleItemType {1577                    collection: collection_id,1578                    owner,1579                    value: (10 as u128).pow(collection.decimal_points as u32)1580                };15811582                Self::add_fungible_item(item)?;1583            },1584            CreateItemData::ReFungible(data) => {1585                let mut owner_list = Vec::new();1586                let value = (10 as u128).pow(collection.decimal_points as u32);1587                owner_list.push(Ownership {owner: owner.clone(), fraction: value});15881589                let item = ReFungibleItemType {1590                    collection: collection_id,1591                    owner: owner_list,1592                    const_data: data.const_data,1593                    variable_data: data.variable_data1594                };15951596                Self::add_refungible_item(item)?;1597            }1598        };15991600        // call event1601        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));16021603        Ok(())1604    }16051606    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1607        let current_index = <ItemListIndex>::get(item.collection)1608            .checked_add(1)1609            .ok_or(Error::<T>::NumOverflow)?;1610        let itemcopy = item.clone();1611        let owner = item.owner.clone();16121613        Self::add_token_index(item.collection, current_index, owner.clone())?;16141615        <ItemListIndex>::insert(item.collection, current_index);1616        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);16171618        // Add current block1619        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1620        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1621        1622        // Update balance1623        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1624            .checked_add(item.value)1625            .ok_or(Error::<T>::NumOverflow)?;1626        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);16271628        Ok(())1629    }16301631    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1632        let current_index = <ItemListIndex>::get(item.collection)1633            .checked_add(1)1634            .ok_or(Error::<T>::NumOverflow)?;1635        let itemcopy = item.clone();16361637        let value = item.owner.first().unwrap().fraction;1638        let owner = item.owner.first().unwrap().owner.clone();16391640        Self::add_token_index(item.collection, current_index, owner.clone())?;16411642        <ItemListIndex>::insert(item.collection, current_index);1643        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);16441645        // Add current block1646        let block_number: T::BlockNumber = 0.into();1647        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);16481649        // Update balance1650        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1651            .checked_add(value)1652            .ok_or(Error::<T>::NumOverflow)?;1653        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);16541655        Ok(())1656    }16571658    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1659        let current_index = <ItemListIndex>::get(item.collection)1660            .checked_add(1)1661            .ok_or(Error::<T>::NumOverflow)?;16621663        let item_owner = item.owner.clone();1664        let collection_id = item.collection.clone();1665        Self::add_token_index(collection_id, current_index, item.owner.clone())?;16661667        <ItemListIndex>::insert(collection_id, current_index);1668        <NftItemList<T>>::insert(collection_id, current_index, item);16691670        // Add current block1671        let block_number: T::BlockNumber = 0.into();1672        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);16731674        // Update balance1675        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1676            .checked_add(1)1677            .ok_or(Error::<T>::NumOverflow)?;1678        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);16791680        Ok(())1681    }16821683    fn burn_refungible_item(1684        collection_id: CollectionId,1685        item_id: TokenId,1686        owner: T::AccountId,1687    ) -> DispatchResult {1688        ensure!(1689            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1690            Error::<T>::TokenNotFound1691        );1692        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1693        let item = collection1694            .owner1695            .iter()1696            .filter(|&i| i.owner == owner)1697            .next()1698            .unwrap();1699        Self::remove_token_index(collection_id, item_id, owner.clone())?;17001701        // remove approve list1702        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));17031704        // update balance1705        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1706            .checked_sub(item.fraction)1707            .ok_or(Error::<T>::NumOverflow)?;1708        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17091710        <ReFungibleItemList<T>>::remove(collection_id, item_id);17111712        Ok(())1713    }17141715    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1716        ensure!(1717            <NftItemList<T>>::contains_key(collection_id, item_id),1718            Error::<T>::TokenNotFound1719        );1720        let item = <NftItemList<T>>::get(collection_id, item_id);1721        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17221723        // remove approve list1724        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));17251726        // update balance1727        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1728            .checked_sub(1)1729            .ok_or(Error::<T>::NumOverflow)?;1730        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1731        <NftItemList<T>>::remove(collection_id, item_id);17321733        Ok(())1734    }17351736    fn burn_fungible_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1737        ensure!(1738            <FungibleItemList<T>>::contains_key(collection_id, item_id),1739            Error::<T>::TokenNotFound1740        );1741        let item = <FungibleItemList<T>>::get(collection_id, item_id);1742        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;17431744        // remove approve list1745        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));17461747        // update balance1748        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1749            .checked_sub(item.value)1750            .ok_or(Error::<T>::NumOverflow)?;1751        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);17521753        <FungibleItemList<T>>::remove(collection_id, item_id);17541755        Ok(())1756    }17571758    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1759        ensure!(1760            <Collection<T>>::contains_key(collection_id),1761            Error::<T>::CollectionNotFound1762        );1763        Ok(())1764    }17651766    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1767        Self::collection_exists(collection_id)?;17681769        let target_collection = <Collection<T>>::get(collection_id);1770        ensure!(1771            subject == target_collection.owner,1772            Error::<T>::NoPermission1773        );17741775        Ok(())1776    }17771778    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1779        let target_collection = <Collection<T>>::get(collection_id);1780        let mut result: bool = subject == target_collection.owner;1781        let exists = <AdminList<T>>::contains_key(collection_id);17821783        if !result & exists {1784            if <AdminList<T>>::get(collection_id).contains(&subject) {1785                result = true1786            }1787        }17881789        result1790    }17911792    fn check_owner_or_admin_permissions(1793        collection_id: CollectionId,1794        subject: T::AccountId,1795    ) -> DispatchResult {1796        Self::collection_exists(collection_id)?;1797        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());17981799        ensure!(1800            result,1801            Error::<T>::NoPermission1802        );1803        Ok(())1804    }18051806    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1807        let target_collection = <Collection<T>>::get(collection_id);18081809        match target_collection.mode {1810            CollectionMode::NFT => {1811                <NftItemList<T>>::get(collection_id, item_id).owner == subject1812            }1813            CollectionMode::Fungible(_) => {1814                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1815            }1816            CollectionMode::ReFungible(_) => {1817                <ReFungibleItemList<T>>::get(collection_id, item_id)1818                    .owner1819                    .iter()1820                    .any(|i| i.owner == subject)1821            }1822            CollectionMode::Invalid => false,1823        }1824    }18251826    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1827        let mes = Error::<T>::AddresNotInWhiteList;1828        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1829        let wl = <WhiteList<T>>::get(collection_id);1830        ensure!(wl.contains(address), mes);18311832        Ok(())1833    }18341835    fn transfer_fungible(1836        collection_id: CollectionId,1837        item_id: TokenId,1838        value: u128,1839        owner: T::AccountId,1840        new_owner: T::AccountId,1841    ) -> DispatchResult {1842        ensure!(1843            <FungibleItemList<T>>::contains_key(collection_id, item_id),1844            Error::<T>::TokenNotFound1845        );18461847        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1848        let amount = full_item.value;18491850        ensure!(amount >= value, Error::<T>::TokenValueTooLow);18511852        // update balance1853        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1854            .checked_sub(value)1855            .ok_or(Error::<T>::NumOverflow)?;1856        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);18571858        let mut new_owner_account_id = 0;1859        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1860        if new_owner_items.len() > 0 {1861            new_owner_account_id = new_owner_items[0];1862        }18631864        // transfer1865        if amount == value && new_owner_account_id == 0 {1866            // change owner1867            // new owner do not have account1868            let mut new_full_item = full_item.clone();1869            new_full_item.owner = new_owner.clone();1870            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18711872            // update balance1873            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1874                .checked_add(value)1875                .ok_or(Error::<T>::NumOverflow)?;1876            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18771878            // update index collection1879            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1880        } else {1881            let mut new_full_item = full_item.clone();1882            new_full_item.value -= value;18831884            // separate amount1885            if new_owner_account_id > 0 {1886                // new owner has account1887                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1888                item.value += value;18891890                // update balance1891                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1892                    .checked_add(value)1893                    .ok_or(Error::<T>::NumOverflow)?;1894                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18951896                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1897            } else {1898                // new owner do not have account1899                let item = FungibleItemType {1900                    collection: collection_id,1901                    owner: new_owner.clone(),1902                    value1903                };19041905                Self::add_fungible_item(item)?;1906            }19071908            if amount == value {1909                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;19101911                // remove approve list1912                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1913                <FungibleItemList<T>>::remove(collection_id, item_id);1914            }19151916            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1917        }19181919        Ok(())1920    }19211922    fn transfer_refungible(1923        collection_id: CollectionId,1924        item_id: TokenId,1925        value: u128,1926        owner: T::AccountId,1927        new_owner: T::AccountId,1928    ) -> DispatchResult {1929        ensure!(1930            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1931            Error::<T>::TokenNotFound1932        );19331934        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1935        let item = full_item1936            .owner1937            .iter()1938            .filter(|i| i.owner == owner)1939            .next()1940            .ok_or(Error::<T>::NumOverflow)?;1941        let amount = item.fraction;19421943        ensure!(amount >= value, Error::<T>::TokenValueTooLow);19441945        // update balance1946        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1947            .checked_sub(value)1948            .ok_or(Error::<T>::NumOverflow)?;1949        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19501951        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1952            .checked_add(value)1953            .ok_or(Error::<T>::NumOverflow)?;1954        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19551956        let old_owner = item.owner.clone();1957        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);19581959        // transfer1960        if amount == value && !new_owner_has_account {1961            // change owner1962            // new owner do not have account1963            let mut new_full_item = full_item.clone();1964            new_full_item1965                .owner1966                .iter_mut()1967                .find(|i| i.owner == owner)1968                .unwrap()1969                .owner = new_owner.clone();1970            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19711972            // update index collection1973            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1974        } else {1975            let mut new_full_item = full_item.clone();1976            new_full_item1977                .owner1978                .iter_mut()1979                .find(|i| i.owner == owner)1980                .unwrap()1981                .fraction -= value;19821983            // separate amount1984            if new_owner_has_account {1985                // new owner has account1986                new_full_item1987                    .owner1988                    .iter_mut()1989                    .find(|i| i.owner == new_owner)1990                    .unwrap()1991                    .fraction += value;1992            } else {1993                // new owner do not have account1994                new_full_item.owner.push(Ownership {1995                    owner: new_owner.clone(),1996                    fraction: value,1997                });1998                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1999            }20002001            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2002        }20032004        Ok(())2005    }20062007    fn transfer_nft(2008        collection_id: CollectionId,2009        item_id: TokenId,2010        sender: T::AccountId,2011        new_owner: T::AccountId,2012    ) -> DispatchResult {2013        ensure!(2014            <NftItemList<T>>::contains_key(collection_id, item_id),2015            Error::<T>::TokenNotFound2016        );20172018        let mut item = <NftItemList<T>>::get(collection_id, item_id);20192020        ensure!(2021            sender == item.owner,2022            Error::<T>::MustBeTokenOwner2023        );20242025        // update balance2026        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2027            .checked_sub(1)2028            .ok_or(Error::<T>::NumOverflow)?;2029        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20302031        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())2032            .checked_add(1)2033            .ok_or(Error::<T>::NumOverflow)?;2034        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);20352036        // change owner2037        let old_owner = item.owner.clone();2038        item.owner = new_owner.clone();2039        <NftItemList<T>>::insert(collection_id, item_id, item);20402041        // update index collection2042        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;20432044        // reset approved list2045        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));2046        Ok(())2047    }2048    2049    fn item_exists(2050        collection_id: CollectionId,2051        item_id: TokenId,2052        mode: &CollectionMode2053    ) -> DispatchResult {2054        match mode {2055            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2056            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2057            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2058            _ => ()2059        };2060        2061        Ok(())2062    }20632064    fn set_re_fungible_variable_data(2065        collection_id: CollectionId,2066        item_id: TokenId,2067        data: Vec<u8>2068    ) -> DispatchResult {2069        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);20702071        item.variable_data = data;20722073        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);20742075        Ok(())2076    }20772078    fn set_nft_variable_data(2079        collection_id: CollectionId,2080        item_id: TokenId,2081        data: Vec<u8>2082    ) -> DispatchResult {2083        let mut item = <NftItemList<T>>::get(collection_id, item_id);2084        2085        item.variable_data = data;20862087        <NftItemList<T>>::insert(collection_id, item_id, item);2088        2089        Ok(())2090    }20912092    fn init_collection(item: &CollectionType<T::AccountId>) {2093        // check params2094        assert!(2095            item.decimal_points <= MAX_DECIMAL_POINTS,2096            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2097        );2098        assert!(2099            item.name.len() <= 64,2100            "Collection name can not be longer than 63 char"2101        );2102        assert!(2103            item.name.len() <= 256,2104            "Collection description can not be longer than 255 char"2105        );2106        assert!(2107            item.token_prefix.len() <= 16,2108            "Token prefix can not be longer than 15 char"2109        );21102111        // Generate next collection ID2112        let next_id = CreatedCollectionCount::get()2113            .checked_add(1)2114            .unwrap();21152116        CreatedCollectionCount::put(next_id);2117    }21182119    fn init_nft_token(item: &NftItemType<T::AccountId>) {2120        let current_index = <ItemListIndex>::get(item.collection)2121            .checked_add(1)2122            .unwrap();21232124        let item_owner = item.owner.clone();2125        let collection_id = item.collection.clone();2126        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();21272128        <ItemListIndex>::insert(collection_id, current_index);21292130        // Update balance2131        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2132            .checked_add(1)2133            .unwrap();2134        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2135    }21362137    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2138        let current_index = <ItemListIndex>::get(item.collection)2139            .checked_add(1)2140            .unwrap();2141        let owner = item.owner.clone();21422143        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();21442145        <ItemListIndex>::insert(item.collection, current_index);21462147        // Update balance2148        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2149            .checked_add(item.value)2150            .unwrap();2151        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2152    }21532154    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2155        let current_index = <ItemListIndex>::get(item.collection)2156            .checked_add(1)2157            .unwrap();21582159        let value = item.owner.first().unwrap().fraction;2160        let owner = item.owner.first().unwrap().owner.clone();21612162        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();21632164        <ItemListIndex>::insert(item.collection, current_index);21652166        // Update balance2167        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2168            .checked_add(value)2169            .unwrap();2170        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2171    }21722173    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {21742175        // add to account limit2176        if <AccountItemCount<T>>::contains_key(owner.clone()) {21772178            // bound Owned tokens by a single address2179            let count = <AccountItemCount<T>>::get(owner.clone());2180            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21812182            <AccountItemCount<T>>::insert(owner.clone(), count2183                .checked_add(1)2184                .ok_or(Error::<T>::NumOverflow)?);2185        }2186        else {2187            <AccountItemCount<T>>::insert(owner.clone(), 1);2188        }21892190        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2191        if list_exists {2192            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2193            let item_contains = list.contains(&item_index.clone());21942195            if !item_contains {2196                list.push(item_index.clone());2197            }21982199            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2200        } else {2201            let mut itm = Vec::new();2202            itm.push(item_index.clone());2203            <AddressTokens<T>>::insert(collection_id, owner, itm);2204            2205        }22062207        Ok(())2208    }22092210    fn remove_token_index(2211        collection_id: CollectionId,2212        item_index: TokenId,2213        owner: T::AccountId,2214    ) -> DispatchResult {22152216        // update counter2217        <AccountItemCount<T>>::insert(owner.clone(), 2218            <AccountItemCount<T>>::get(owner.clone())2219            .checked_sub(1)2220            .ok_or(Error::<T>::NumOverflow)?);222122222223        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2224        if list_exists {2225            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2226            let item_contains = list.contains(&item_index.clone());22272228            if item_contains {2229                list.retain(|&item| item != item_index);2230                <AddressTokens<T>>::insert(collection_id, owner, list);2231            }2232        }22332234        Ok(())2235    }22362237    fn move_token_index(2238        collection_id: CollectionId,2239        item_index: TokenId,2240        old_owner: T::AccountId,2241        new_owner: T::AccountId,2242    ) -> DispatchResult {2243        Self::remove_token_index(collection_id, item_index, old_owner)?;2244        Self::add_token_index(collection_id, item_index, new_owner)?;22452246        Ok(())2247    }2248}22492250////////////////////////////////////////////////////////////////////////////////////////////////////2251// Economic models2252// #region22532254/// Fee multiplier.2255pub type Multiplier = FixedU128;22562257type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2258    <T as system::Trait>::AccountId,2259>>::Balance;2260type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2261    <T as system::Trait>::AccountId,2262>>::NegativeImbalance;22632264/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2265/// in the queue.2266#[derive(Encode, Decode, Clone, Eq, PartialEq)]2267pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2268    #[codec(compact)] BalanceOf<T>2269);22702271impl<T: Trait + Send + Sync> sp_std::fmt::Debug2272    for ChargeTransactionPayment<T>2273{2274    #[cfg(feature = "std")]2275    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2276        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2277    }2278    #[cfg(not(feature = "std"))]2279    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2280        Ok(())2281    }2282}22832284impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2285where2286    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2287    BalanceOf<T>: Send + Sync + FixedPointOperand,2288{2289    /// utility constructor. Used only in client/factory code.2290    pub fn from(fee: BalanceOf<T>) -> Self {2291        Self(fee)2292    }22932294    pub fn traditional_fee(2295        len: usize,2296        info: &DispatchInfoOf<T::Call>,2297        tip: BalanceOf<T>,2298    ) -> BalanceOf<T>2299    where2300        T::Call: Dispatchable<Info = DispatchInfo>,2301    {2302        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2303    }23042305    fn withdraw_fee(2306        &self,2307        who: &T::AccountId,2308        call: &T::Call,2309        info: &DispatchInfoOf<T::Call>,2310        len: usize,2311    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2312        let tip = self.0;23132314        // Set fee based on call type. Creating collection costs 1 Unique.2315        // All other transactions have traditional fees so far2316        // let fee = match call.is_sub_type() {2317        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2318        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2319        //                                                 // _ => <BalanceOf<T>>::from(100)2320        // };2321        let fee = Self::traditional_fee(len, info, tip);23222323        // Determine who is paying transaction fee based on ecnomic model2324        // Parse call to extract collection ID and access collection sponsor2325        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2326            Some(Call::create_item(collection_id, _owner, _properties)) => {23272328                // check free create limit2329                if <Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)2330                {2331                    <Collection<T>>::get(collection_id).sponsor2332                } else {2333                    T::AccountId::default()2334                }2335            }2336            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2337                2338                let _collection_limits = <Collection<T>>::get(collection_id).limits;2339                let _collection_mode = <Collection<T>>::get(collection_id).mode;23402341                // sponsor timeout2342                let sponsor_transfer = match _collection_mode {2343                    CollectionMode::NFT => {23442345                        // get correct limit2346                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2347                            _collection_limits.sponsor_transfer_timeout2348                        } else {2349                            ChainLimit::get().nft_sponsor_transfer_timeout2350                        };23512352                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2353                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2354                        let limit_time = basket + limit.into();2355                        if block_number >= limit_time {2356                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2357                            true2358                        }2359                        else {2360                            false2361                        }2362                    }2363                    CollectionMode::Fungible(_) => {23642365                        // get correct limit2366                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2367                            _collection_limits.sponsor_transfer_timeout2368                        } else {2369                            ChainLimit::get().fungible_sponsor_transfer_timeout2370                        };23712372                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2373                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2374                        if basket.iter().any(|i| i.address == _new_owner.clone())2375                        {2376                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2377                            let limit_time = item.start_block + limit.into();2378                            if block_number >= limit_time {2379                                basket.retain(|x| x.address == item.address);2380                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2381                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2382                                true2383                            }2384                            else {2385                                false2386                            }2387                        }2388                        else {2389                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2390                            true2391                        }2392                    }2393                    CollectionMode::ReFungible(_) => {23942395                        // get correct limit2396                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2397                            _collection_limits.sponsor_transfer_timeout2398                        } else {2399                            ChainLimit::get().refungible_sponsor_transfer_timeout2400                        };24012402                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2403                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2404                        let limit_time = basket + limit.into();2405                        if block_number >= limit_time {2406                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2407                            true2408                        } else {2409                            false2410                        }2411                    }2412                    _ => {2413                        false2414                    },2415                };24162417                if !sponsor_transfer {2418                    T::AccountId::default()2419                } else {2420                    <Collection<T>>::get(collection_id).sponsor2421                }2422            }24232424            _ => T::AccountId::default(),2425        };24262427        // Sponsor smart contracts2428        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {24292430            // On instantiation: set the contract owner2431            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {24322433                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2434                    code_hash,2435                    &data,2436                    &who,2437                );2438                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());24392440                T::AccountId::default()2441            },24422443            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2444            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {24452446                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());24472448                let mut sponsor_transfer = false;2449                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2450                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2451                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2452                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2453                    let limit_time = last_tx_block + rate_limit;24542455                    if block_number >= limit_time {2456                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2457                        sponsor_transfer = true;2458                    }2459                } else {2460                    sponsor_transfer = false;2461                }2462               2463                2464                let mut sp = T::AccountId::default();2465                if sponsor_transfer {2466                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2467                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2468                            sp = called_contract;2469                        }2470                    }2471                }24722473                sp2474            },24752476            _ => sponsor,2477        };24782479        let mut who_pays_fee: T::AccountId = sponsor.clone();2480        if sponsor == T::AccountId::default() {2481            who_pays_fee = who.clone();2482        }24832484        // Only mess with balances if fee is not zero.2485        if fee.is_zero() {2486            return Ok((fee, None));2487        }24882489        match <T as transaction_payment::Trait>::Currency::withdraw(2490            &who_pays_fee,2491            fee,2492            if tip.is_zero() {2493                WithdrawReason::TransactionPayment.into()2494            } else {2495                WithdrawReason::TransactionPayment | WithdrawReason::Tip2496            },2497            ExistenceRequirement::KeepAlive,2498        ) {2499            Ok(imbalance) => Ok((fee, Some(imbalance))),2500            Err(_) => Err(InvalidTransaction::Payment.into()),2501        }2502    }2503}250425052506impl<T: Trait + Send + Sync> SignedExtension2507    for ChargeTransactionPayment<T>2508where2509    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2510    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2511{2512    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2513    type AccountId = T::AccountId;2514    type Call = T::Call;2515    type AdditionalSigned = ();2516    type Pre = (2517        BalanceOf<T>,2518        Self::AccountId,2519        Option<NegativeImbalanceOf<T>>,2520        BalanceOf<T>,2521    );2522    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2523        Ok(())2524    }25252526    fn validate(2527        &self,2528        _who: &Self::AccountId,2529        _call: &Self::Call,2530        _info: &DispatchInfoOf<Self::Call>,2531        _len: usize,2532    ) -> TransactionValidity {2533        Ok(ValidTransaction::default())2534    }25352536    fn pre_dispatch(2537        self,2538        who: &Self::AccountId,2539        call: &Self::Call,2540        info: &DispatchInfoOf<Self::Call>,2541        len: usize,2542    ) -> Result<Self::Pre, TransactionValidityError> {2543        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2544        Ok((self.0, who.clone(), imbalance, fee))2545    }25462547    fn post_dispatch(2548        pre: Self::Pre,2549        info: &DispatchInfoOf<Self::Call>,2550        post_info: &PostDispatchInfoOf<Self::Call>,2551        len: usize,2552        _result: &DispatchResult,2553    ) -> Result<(), TransactionValidityError> {2554        let (tip, who, imbalance, fee) = pre;2555        if let Some(payed) = imbalance {2556            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2557                len as u32, info, post_info, tip,2558            );2559            let refund = fee.saturating_sub(actual_fee);2560            let actual_payment =2561                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2562                    &who, refund,2563                ) {2564                    Ok(refund_imbalance) => {2565                        // The refund cannot be larger than the up front payed max weight.2566                        // `PostDispatchInfo::calc_unspent` guards against such a case.2567                        match payed.offset(refund_imbalance) {2568                            Ok(actual_payment) => actual_payment,2569                            Err(_) => return Err(InvalidTransaction::Payment.into()),2570                        }2571                    }2572                    // We do not recreate the account using the refund. The up front payment2573                    // is gone in that case.2574                    Err(_) => payed,2575                };2576            let imbalances = actual_payment.split(tip);2577            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2578                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2579            );2580        }2581        Ok(())2582    }2583}25842585// #endregion
modifiedpallets/nft/src/tests.rsdiffbeforeafterboth
--- a/pallets/nft/src/tests.rs
+++ b/pallets/nft/src/tests.rs
@@ -77,6 +77,19 @@
 
 // Use cases tests region
 // #region
+
+#[test]
+fn set_version_schema() {
+    new_test_ext().execute_with(|| {
+        default_limits();
+        let origin1 = Origin::signed(1);
+        let collection_id = create_test_collection(&CollectionMode::NFT, 1);
+        
+        assert_ok!(TemplateModule::set_schema_version(origin1, collection_id, SchemaVersion::Unique));
+        assert_eq!(TemplateModule::collection(collection_id).schema_version, SchemaVersion::Unique);
+    });
+}
+
 #[test]
 fn create_fungible_collection_fails_with_large_decimal_numbers() {
     new_test_ext().execute_with(|| {