git.delta.rocks / unique-network / refs/commits / 82ca9c1be6da

difftreelog

Merge pull request #25 from usetech-llc/feature/NFTPAR-110_contract_spam

str-mv2020-12-02parents: #61a5f4d #33c31e8.patch.diff
in: master
Feature/nftpar 110 contract spam protection

4 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3737,7 +3737,10 @@
  "frame-support",
  "frame-system",
  "log",
+ "pallet-balances",
  "pallet-contracts",
+ "pallet-randomness-collective-flip",
+ "pallet-timestamp",
  "pallet-transaction-payment",
  "parity-scale-codec",
  "serde",
modifiedpallets/nft/src/default_weights.rsdiffbeforeafterboth
--- a/pallets/nft/src/default_weights.rs
+++ b/pallets/nft/src/default_weights.rs
@@ -107,9 +107,19 @@
             .saturating_add(DbWeight::get().reads(2 as Weight))
             .saturating_add(DbWeight::get().writes(1 as Weight))
     }
+    // fn set_chain_limits() -> Weight {
+    //     (0 as Weight)
+    //         .saturating_add(DbWeight::get().reads(1 as Weight))
+    //         .saturating_add(DbWeight::get().writes(1 as Weight))
+    // }
     // fn enable_contract_sponsoring() -> Weight {
     //     (0 as Weight)
     //         .saturating_add(DbWeight::get().reads(1 as Weight))
     //         .saturating_add(DbWeight::get().writes(1 as Weight))
     // }
+    // fn set_contract_sponsoring_rate_limit() -> Weight {
+    //     (0 as Weight)
+    //         .saturating_add(DbWeight::get().reads(1 as Weight))
+    //         .saturating_add(DbWeight::get().writes(1 as Weight))
+    // }
 }
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;4748// Structs49// #region5051#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]52#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]53pub enum CollectionMode {54    Invalid,55    NFT,56    // decimal points57    Fungible(u32),58    // decimal points59    ReFungible(u32),60}6162impl Into<u8> for CollectionMode {63    fn into(self) -> u8 {64        match self {65            CollectionMode::Invalid => 0,66            CollectionMode::NFT => 1,67            CollectionMode::Fungible(_) => 2,68            CollectionMode::ReFungible(_) => 3,69        }70    }71}7273#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]74#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]75pub enum AccessMode {76    Normal,77    WhiteList,78}79impl Default for AccessMode {80    fn default() -> Self {81        Self::Normal82    }83}8485impl Default for CollectionMode {86    fn default() -> Self {87        Self::Invalid88    }89}9091#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]92#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]93pub struct Ownership<AccountId> {94    pub owner: AccountId,95    pub fraction: u128,96}9798#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]99#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]100pub struct CollectionType<AccountId> {101    pub owner: AccountId,102    pub mode: CollectionMode,103    pub access: AccessMode,104    pub decimal_points: u32,105    pub name: Vec<u16>,        // 64 include null escape char106    pub description: Vec<u16>, // 256 include null escape char107    pub token_prefix: Vec<u8>, // 16 include null escape char108    pub mint_mode: bool,109    pub offchain_schema: Vec<u8>,110    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender111    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship112    pub variable_on_chain_schema: Vec<u8>, //113    pub const_on_chain_schema: Vec<u8>, //114}115116#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]117#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]118pub struct NftItemType<AccountId> {119    pub collection: u64,120    pub owner: AccountId,121    pub const_data: Vec<u8>,122    pub variable_data: Vec<u8>,123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct FungibleItemType<AccountId> {128    pub collection: u64,129    pub owner: AccountId,130    pub value: u128,131}132133#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]134#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]135pub struct ReFungibleItemType<AccountId> {136    pub collection: u64,137    pub owner: Vec<Ownership<AccountId>>,138    pub const_data: Vec<u8>,139    pub variable_data: Vec<u8>,140}141142#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]143#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]144pub struct ApprovePermissions<AccountId> {145    pub approved: AccountId,146    pub amount: u64,147}148149#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]150#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]151pub struct VestingItem<AccountId, Moment> {152    pub sender: AccountId,153    pub recipient: AccountId,154    pub collection_id: u64,155    pub item_id: u64,156    pub amount: u64,157    pub vesting_date: Moment,158}159160#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]161#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]162pub struct BasketItem<AccountId, BlockNumber> {163    pub address: AccountId,164    pub start_block: BlockNumber,165}166167#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]168#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169pub struct ChainLimits {170    pub collection_numbers_limit: u64,171    pub account_token_ownership_limit: u64,172    pub collections_admins_limit: u64,173    pub custom_data_limit: u32,174175    // Timeouts for item types in passed blocks176    pub nft_sponsor_transfer_timeout: u32,177    pub fungible_sponsor_transfer_timeout: u32,178    pub refungible_sponsor_transfer_timeout: u32,179}180181pub trait WeightInfo {182	fn create_collection() -> Weight;183	fn destroy_collection() -> Weight;184	fn add_to_white_list() -> Weight;185	fn remove_from_white_list() -> Weight;186    fn set_public_access_mode() -> Weight;187    fn set_mint_permission() -> Weight;188    fn change_collection_owner() -> Weight;189    fn add_collection_admin() -> Weight;190    fn remove_collection_admin() -> Weight;191    fn set_collection_sponsor() -> Weight;192    fn confirm_sponsorship() -> Weight;193    fn remove_collection_sponsor() -> Weight;194    fn create_item(s: usize) -> Weight;195    fn burn_item() -> Weight;196    fn transfer() -> Weight;197    fn approve() -> Weight;198    fn transfer_from() -> Weight;199    fn set_offchain_schema() -> Weight;200    fn set_const_on_chain_schema() -> Weight;201    fn set_variable_on_chain_schema() -> Weight;202    fn set_variable_meta_data() -> Weight;203    // fn enable_contract_sponsoring() -> Weight;204}205206#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]207#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]208pub struct CreateNftData {209    pub const_data: Vec<u8>,210    pub variable_data: Vec<u8>,211}212213#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]214#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]215pub struct CreateFungibleData {216}217218#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]219#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]220pub struct CreateReFungibleData {221    pub const_data: Vec<u8>,222    pub variable_data: Vec<u8>,223}224225#[derive(Encode, Decode, Debug, Clone, PartialEq)]226#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]227pub enum CreateItemData {228    NFT(CreateNftData),229    Fungible(CreateFungibleData),230    ReFungible(CreateReFungibleData)231}232233impl CreateItemData {234    pub fn len(&self) -> usize {235        let len = match self {236            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),237            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),238            _ => 0239        };240        241        return len;242    }243}244245impl From<CreateNftData> for CreateItemData {246    fn from(item: CreateNftData) -> Self {247        CreateItemData::NFT(item)248    }249}250251impl From<CreateReFungibleData> for CreateItemData {252    fn from(item: CreateReFungibleData) -> Self {253        CreateItemData::ReFungible(item)254    }255}256257impl From<CreateFungibleData> for CreateItemData {258    fn from(item: CreateFungibleData) -> Self {259        CreateItemData::Fungible(item)260    }261}262263264decl_error! {265	/// Error for non-fungible-token module.266	pub enum Error for Module<T: Trait> {267        /// Total collections bound exceeded268        TotalCollectionsLimitExceeded,269		/// Decimal_points parameter must be lower than 4270        CollectionDecimalPointLimitExceeded, 271        /// Collection name can not be longer than 63 char272        CollectionNameLimitExceeded, 273        /// Collection description can not be longer than 255 char274        CollectionDescriptionLimitExceeded, 275        /// Token prefix can not be longer than 15 char276        CollectionTokenPrefixLimitExceeded,277        /// This collection does not exist278        CollectionNotFound,279        /// Item not exists280        TokenNotFound,281        /// Arithmetic calculation overflow282        NumOverflow,       283        /// Account already has admin role284        AlreadyAdmin,  285        /// You do not own this collection286        NoPermission,287        /// This address is not set as sponsor, use setCollectionSponsor first288        ConfirmUnsetSponsorFail,289        /// Collection is not in mint mode290        PublicMintingNotAllowed,291        /// Sender parameter and item owner must be equal292        MustBeTokenOwner,293        /// Item balance not enouth294        TokenValueTooLow,295        /// Size of item is too large296        NftSizeLimitExceeded,297        /// Size of item must be 0 with fungible type298        FungibleUnexpectedParam,299        /// No approve found300        ApproveNotFound,301        /// Requested value more than approved302        TokenValueNotEnough,303        /// Only approved addresses can call this method304        ApproveRequired,305        /// Address is not in white list306        AddresNotInWhiteList,307        /// Number of collection admins bound exceeded308        CollectionAdminsLimitExceeded,309        /// Owned tokens by a single address bound exceeded310        AddressOwnershipLimitExceeded,311        /// Length of items properties must be greater than 0312        EmptyArgument,313	}314}315316pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {317    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;318319    /// Weight information for extrinsics in this pallet.320	type WeightInfo: WeightInfo;321}322323#[cfg(feature = "runtime-benchmarks")]324mod benchmarking;325326// #endregion327328decl_storage! {329    trait Store for Module<T: Trait> as Nft {330331        // Private members332        NextCollectionID: u64;333        CreatedCollectionCount: u64;334        ChainVersion: u64;335        ItemListIndex: map hasher(blake2_128_concat) u64 => u64;336337        // Chain limits struct338        pub ChainLimit get(fn chain_limit) config(): ChainLimits;339340        // Bound counters341        CollectionCount: u64;342        pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;343344        // Basic collections345        pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;346        pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;347        pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;348349        /// Balance owner per collection map350        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;351352        /// second parameter: item id + owner account id353        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;354355        /// Item collections356        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;357        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;358        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;359360        /// Index list361        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;362363        /// Tokens transfer baskets364        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;365        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => Vec<BasketItem<T::AccountId, T::BlockNumber>>;366        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;367368        // Contract Sponsorship and Ownership369        pub ContractOwner get(fn contract_owner): map hasher(identity) T::AccountId => T::AccountId;370        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(identity) T::AccountId => bool;371    }372    add_extra_genesis {373        build(|config: &GenesisConfig<T>| {374            // Modification of storage375            for (_num, _c) in &config.collection {376                <Module<T>>::init_collection(_c);377            }378379            for (_num, _q, _i) in &config.nft_item_id {380                <Module<T>>::init_nft_token(_i);381            }382383            for (_num, _q, _i) in &config.fungible_item_id {384                <Module<T>>::init_fungible_token(_i);385            }386387            for (_num, _q, _i) in &config.refungible_item_id {388                <Module<T>>::init_refungible_token(_i);389            }390        })391    }392}393394decl_event!(395    pub enum Event<T>396    where397        AccountId = <T as system::Trait>::AccountId,398    {399        /// New collection was created400        /// 401        /// # Arguments402        /// 403        /// * collection_id: Globally unique identifier of newly created collection.404        /// 405        /// * mode: [CollectionMode] converted into u8.406        /// 407        /// * account_id: Collection owner.408        Created(u64, u8, AccountId),409410        /// New item was created.411        /// 412        /// # Arguments413        /// 414        /// * collection_id: Id of the collection where item was created.415        /// 416        /// * item_id: Id of an item. Unique within the collection.417        ItemCreated(u64, u64),418419        /// Collection item was burned.420        /// 421        /// # Arguments422        /// 423        /// collection_id.424        /// 425        /// item_id: Identifier of burned NFT.426        ItemDestroyed(u64, u64),427    }428);429430decl_module! {431    pub struct Module<T: Trait> for enum Call where origin: T::Origin {432433        fn deposit_event() = default;434        type Error = Error<T>;435436        fn on_initialize(now: T::BlockNumber) -> Weight {437438            if ChainVersion::get() < 2439            {440                let value = NextCollectionID::get();441                CreatedCollectionCount::put(value);442                ChainVersion::put(2);443            }444445            0446        }447448        /// 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.449        /// 450        /// # Permissions451        /// 452        /// * Anyone.453        /// 454        /// # Arguments455        /// 456        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.457        /// 458        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.459        /// 460        /// * token_prefix: UTF-8 string with token prefix.461        /// 462        /// * mode: [CollectionMode] collection type and type dependent data.463        // returns collection ID464        #[weight = T::WeightInfo::create_collection()]465        pub fn create_collection(origin,466                                 collection_name: Vec<u16>,467                                 collection_description: Vec<u16>,468                                 token_prefix: Vec<u8>,469                                 mode: CollectionMode) -> DispatchResult {470471            // Anyone can create a collection472            let who = ensure_signed(origin)?;473474            let decimal_points = match mode {475                CollectionMode::Fungible(points) => points,476                CollectionMode::ReFungible(points) => points,477                _ => 0478            };479480            // bound Total number of collections481            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);482483            // check params484            ensure!(decimal_points <= 4, Error::<T>::CollectionDecimalPointLimitExceeded);485486            let mut name = collection_name.to_vec();487            name.push(0);488            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);489490            let mut description = collection_description.to_vec();491            description.push(0);492            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);493494            let mut prefix = token_prefix.to_vec();495            prefix.push(0);496            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);497498            // Generate next collection ID499            let next_id = CreatedCollectionCount::get()500                .checked_add(1)501                .ok_or(Error::<T>::NumOverflow)?;502503            // bound counter504            let total = CollectionCount::get()505                .checked_add(1)506                .ok_or(Error::<T>::NumOverflow)?;507508            CreatedCollectionCount::put(next_id);509            CollectionCount::put(total);510511            // Create new collection512            let new_collection = CollectionType {513                owner: who.clone(),514                name: name,515                mode: mode.clone(),516                mint_mode: false,517                access: AccessMode::Normal,518                description: description,519                decimal_points: decimal_points,520                token_prefix: prefix,521                offchain_schema: Vec::new(),522                sponsor: T::AccountId::default(),523                unconfirmed_sponsor: T::AccountId::default(),524                variable_on_chain_schema: Vec::new(),525                const_on_chain_schema: Vec::new(),526            };527528            // Add new collection to map529            <Collection<T>>::insert(next_id, new_collection);530531            // call event532            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));533534            Ok(())535        }536537        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.538        /// 539        /// # Permissions540        /// 541        /// * Collection Owner.542        /// 543        /// # Arguments544        /// 545        /// * collection_id: collection to destroy.546        #[weight = T::WeightInfo::destroy_collection()]547        pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {548549            let sender = ensure_signed(origin)?;550            Self::check_owner_permissions(collection_id, sender)?;551552            <AddressTokens<T>>::remove_prefix(collection_id);553            <ApprovedList<T>>::remove_prefix(collection_id);554            <Balance<T>>::remove_prefix(collection_id);555            <ItemListIndex>::remove(collection_id);556            <AdminList<T>>::remove(collection_id);557            <Collection<T>>::remove(collection_id);558            <WhiteList<T>>::remove(collection_id);559560            <NftItemList<T>>::remove_prefix(collection_id);561            <FungibleItemList<T>>::remove_prefix(collection_id);562            <ReFungibleItemList<T>>::remove_prefix(collection_id);563564            <NftTransferBasket<T>>::remove_prefix(collection_id);565            <FungibleTransferBasket<T>>::remove_prefix(collection_id);566            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);567568            if CollectionCount::get() > 0569            {570                // bound couter571                let total = CollectionCount::get()572                    .checked_sub(1)573                    .ok_or(Error::<T>::NumOverflow)?;574575                CollectionCount::put(total);576            }577578            Ok(())579        }580581        /// Add an address to white list.582        /// 583        /// # Permissions584        /// 585        /// * Collection Owner586        /// * Collection Admin587        /// 588        /// # Arguments589        /// 590        /// * collection_id.591        /// 592        /// * address.593        #[weight = T::WeightInfo::add_to_white_list()]594        pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{595596            let sender = ensure_signed(origin)?;597            Self::check_owner_or_admin_permissions(collection_id, sender)?;598599            let mut white_list_collection: Vec<T::AccountId>;600            if <WhiteList<T>>::contains_key(collection_id) {601                white_list_collection = <WhiteList<T>>::get(collection_id);602                if !white_list_collection.contains(&address.clone())603                {604                    white_list_collection.push(address.clone());605                }606            }607            else {608                white_list_collection = Vec::new();609                white_list_collection.push(address.clone());610            }611612            <WhiteList<T>>::insert(collection_id, white_list_collection);613            Ok(())614        }615616        /// Remove an address from white list.617        /// 618        /// # Permissions619        /// 620        /// * Collection Owner621        /// * Collection Admin622        /// 623        /// # Arguments624        /// 625        /// * collection_id.626        /// 627        /// * address.628        #[weight = T::WeightInfo::remove_from_white_list()]629        pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{630631            let sender = ensure_signed(origin)?;632            Self::check_owner_or_admin_permissions(collection_id, sender)?;633634            if <WhiteList<T>>::contains_key(collection_id) {635                let mut white_list_collection = <WhiteList<T>>::get(collection_id);636                if white_list_collection.contains(&address.clone())637                {638                    white_list_collection.retain(|i| *i != address.clone());639                    <WhiteList<T>>::insert(collection_id, white_list_collection);640                }641            }642643            Ok(())644        }645646        /// Toggle between normal and white list access for the methods with access for `Anyone`.647        /// 648        /// # Permissions649        /// 650        /// * Collection Owner.651        /// 652        /// # Arguments653        /// 654        /// * collection_id.655        /// 656        /// * mode: [AccessMode]657        #[weight = T::WeightInfo::set_public_access_mode()]658        pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult659        {660            let sender = ensure_signed(origin)?;661662            Self::check_owner_permissions(collection_id, sender)?;663            let mut target_collection = <Collection<T>>::get(collection_id);664            target_collection.access = mode;665            <Collection<T>>::insert(collection_id, target_collection);666667            Ok(())668        }669670        /// Allows Anyone to create tokens if:671        /// * White List is enabled, and672        /// * Address is added to white list, and673        /// * This method was called with True parameter674        /// 675        /// # Permissions676        /// * Collection Owner677        ///678        /// # Arguments679        /// 680        /// * collection_id.681        /// 682        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.683        #[weight = T::WeightInfo::set_mint_permission()]684        pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult685        {686            let sender = ensure_signed(origin)?;687688            Self::check_owner_permissions(collection_id, sender)?;689            let mut target_collection = <Collection<T>>::get(collection_id);690            target_collection.mint_mode = mint_permission;691            <Collection<T>>::insert(collection_id, target_collection);692693            Ok(())694        }695696        /// Change the owner of the collection.697        /// 698        /// # Permissions699        /// 700        /// * Collection Owner.701        /// 702        /// # Arguments703        /// 704        /// * collection_id.705        /// 706        /// * new_owner.707        #[weight = T::WeightInfo::change_collection_owner()]708        pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {709710            let sender = ensure_signed(origin)?;711            Self::check_owner_permissions(collection_id, sender)?;712            let mut target_collection = <Collection<T>>::get(collection_id);713            target_collection.owner = new_owner;714            <Collection<T>>::insert(collection_id, target_collection);715716            Ok(())717        }718719        /// Adds an admin of the Collection.720        /// 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. 721        /// 722        /// # Permissions723        /// 724        /// * Collection Owner.725        /// * Collection Admin.726        /// 727        /// # Arguments728        /// 729        /// * collection_id: ID of the Collection to add admin for.730        /// 731        /// * new_admin_id: Address of new admin to add.732        #[weight = T::WeightInfo::add_collection_admin()]733        pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {734735            let sender = ensure_signed(origin)?;736            Self::check_owner_or_admin_permissions(collection_id, sender)?;737            let mut admin_arr: Vec<T::AccountId> = Vec::new();738739            if <AdminList<T>>::contains_key(collection_id)740            {741                admin_arr = <AdminList<T>>::get(collection_id);742                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);743            }744745            // Number of collection admins746            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);747748            admin_arr.push(new_admin_id);749            <AdminList<T>>::insert(collection_id, admin_arr);750751            Ok(())752        }753754        /// 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.755        ///756        /// # Permissions757        /// 758        /// * Collection Owner.759        /// * Collection Admin.760        /// 761        /// # Arguments762        /// 763        /// * collection_id: ID of the Collection to remove admin for.764        /// 765        /// * account_id: Address of admin to remove.766        #[weight = T::WeightInfo::remove_collection_admin()]767        pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {768769            let sender = ensure_signed(origin)?;770            Self::check_owner_or_admin_permissions(collection_id, sender)?;771772            if <AdminList<T>>::contains_key(collection_id)773            {774                let mut admin_arr = <AdminList<T>>::get(collection_id);775                admin_arr.retain(|i| *i != account_id);776                <AdminList<T>>::insert(collection_id, admin_arr);777            }778779            Ok(())780        }781782        /// # Permissions783        /// 784        /// * Collection Owner785        /// 786        /// # Arguments787        /// 788        /// * collection_id.789        /// 790        /// * new_sponsor.791        #[weight = T::WeightInfo::set_collection_sponsor()]792        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {793794            let sender = ensure_signed(origin)?;795            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);796797            let mut target_collection = <Collection<T>>::get(collection_id);798            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);799800            target_collection.unconfirmed_sponsor = new_sponsor;801            <Collection<T>>::insert(collection_id, target_collection);802803            Ok(())804        }805806        /// # Permissions807        /// 808        /// * Sponsor.809        /// 810        /// # Arguments811        /// 812        /// * collection_id.813        #[weight = T::WeightInfo::confirm_sponsorship()]814        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {815816            let sender = ensure_signed(origin)?;817            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);818819            let mut target_collection = <Collection<T>>::get(collection_id);820            ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);821822            target_collection.sponsor = target_collection.unconfirmed_sponsor;823            target_collection.unconfirmed_sponsor = T::AccountId::default();824            <Collection<T>>::insert(collection_id, target_collection);825826            Ok(())827        }828829        /// Switch back to pay-per-own-transaction model.830        ///831        /// # Permissions832        ///833        /// * Collection owner.834        /// 835        /// # Arguments836        /// 837        /// * collection_id.838        #[weight = T::WeightInfo::remove_collection_sponsor()]839        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {840841            let sender = ensure_signed(origin)?;842            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);843844            let mut target_collection = <Collection<T>>::get(collection_id);845            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);846847            target_collection.sponsor = T::AccountId::default();848            <Collection<T>>::insert(collection_id, target_collection);849850            Ok(())851        }852853        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.854        /// 855        /// # Permissions856        /// 857        /// * Collection Owner.858        /// * Collection Admin.859        /// * Anyone if860        ///     * White List is enabled, and861        ///     * Address is added to white list, and862        ///     * MintPermission is enabled (see SetMintPermission method)863        /// 864        /// # Arguments865        /// 866        /// * collection_id: ID of the collection.867        /// 868        /// * owner: Address, initial owner of the NFT.869        ///870        /// * data: Token data to store on chain.871        // #[weight =872        // (130_000_000 as Weight)873        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))874        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))875        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]876877        #[weight = T::WeightInfo::create_item(data.len())]878        pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {879880            let sender = ensure_signed(origin)?;881882            Self::collection_exists(collection_id)?;883884            let target_collection = <Collection<T>>::get(collection_id);885886            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;887            Self::validate_create_item_args(&target_collection, &data)?;888            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;889890            Ok(())891        }892893        /// This method creates multiple instances of NFT Collection created with CreateCollection method.894        /// 895        /// # Permissions896        /// 897        /// * Collection Owner.898        /// * Collection Admin.899        /// * Anyone if900        ///     * White List is enabled, and901        ///     * Address is added to white list, and902        ///     * MintPermission is enabled (see SetMintPermission method)903        /// 904        /// # Arguments905        /// 906        /// * collection_id: ID of the collection.907        /// 908        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].909        /// 910        /// * owner: Address, initial owner of the NFT.911        #[weight = T::WeightInfo::create_item(items_data.into_iter()912                               .map(|data| { data.len() })913                               .sum())]914        pub fn create_multiple_items(origin, collection_id: u64, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {915916            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);917            let sender = ensure_signed(origin)?;918919            Self::collection_exists(collection_id)?;920            let target_collection = <Collection<T>>::get(collection_id);921922            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;923924            for data in &items_data {925                Self::validate_create_item_args(&target_collection, data)?;926            }927            for data in &items_data {928                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;929            }930931            Ok(())932        }933934        /// Destroys a concrete instance of NFT.935        /// 936        /// # Permissions937        /// 938        /// * Collection Owner.939        /// * Collection Admin.940        /// * Current NFT Owner.941        /// 942        /// # Arguments943        /// 944        /// * collection_id: ID of the collection.945        /// 946        /// * item_id: ID of NFT to burn.947        #[weight = T::WeightInfo::burn_item()]948        pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {949950            let sender = ensure_signed(origin)?;951            Self::collection_exists(collection_id)?;952953            // Transfer permissions check954            let target_collection = <Collection<T>>::get(collection_id);955            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||956                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),957                Error::<T>::NoPermission);958959            if target_collection.access == AccessMode::WhiteList {960                Self::check_white_list(collection_id, &sender)?;961            }962963            match target_collection.mode964            {965                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,966                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,967                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,968                _ => ()969            };970971            // call event972            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));973974            Ok(())975        }976977        /// Change ownership of the token.978        /// 979        /// # Permissions980        /// 981        /// * Collection Owner982        /// * Collection Admin983        /// * Current NFT owner984        ///985        /// # Arguments986        /// 987        /// * recipient: Address of token recipient.988        /// 989        /// * collection_id.990        /// 991        /// * item_id: ID of the item992        ///     * Non-Fungible Mode: Required.993        ///     * Fungible Mode: Ignored.994        ///     * Re-Fungible Mode: Required.995        /// 996        /// * value: Amount to transfer.997        ///     * Non-Fungible Mode: Ignored998        ///     * Fungible Mode: Must specify transferred amount999        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1000        #[weight = T::WeightInfo::transfer()]1001        pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {10021003            let sender = ensure_signed(origin)?;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                Self::check_white_list(collection_id, &recipient)?;1014            }10151016            match target_collection.mode1017            {1018                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1019                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1020                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1021                _ => ()1022            };10231024            Ok(())1025        }10261027        /// Set, change, or remove approved address to transfer the ownership of the NFT.1028        /// 1029        /// # Permissions1030        /// 1031        /// * Collection Owner1032        /// * Collection Admin1033        /// * Current NFT owner1034        /// 1035        /// # Arguments1036        /// 1037        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1038        /// 1039        /// * collection_id.1040        /// 1041        /// * item_id: ID of the item.1042        #[weight = T::WeightInfo::approve()]1043        pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {10441045            let sender = ensure_signed(origin)?;10461047            // Transfer permissions check1048            let target_collection = <Collection<T>>::get(collection_id);1049            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1050                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1051                Error::<T>::NoPermission);10521053            if target_collection.access == AccessMode::WhiteList {1054                Self::check_white_list(collection_id, &sender)?;1055                Self::check_white_list(collection_id, &approved)?;1056            }10571058            // amount param stub1059            let amount = 100000000;10601061            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1062            if list_exists {10631064                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1065                let item_contains = list.iter().any(|i| i.approved == approved);10661067                if !item_contains {1068                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1069                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1070                }1071            } else {10721073                let mut list = Vec::new();1074                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1075                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1076            }10771078            Ok(())1079        }1080        1081        /// 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.1082        /// 1083        /// # Permissions1084        /// * Collection Owner1085        /// * Collection Admin1086        /// * Current NFT owner1087        /// * Address approved by current NFT owner1088        /// 1089        /// # Arguments1090        /// 1091        /// * from: Address that owns token.1092        /// 1093        /// * recipient: Address of token recipient.1094        /// 1095        /// * collection_id.1096        /// 1097        /// * item_id: ID of the item.1098        /// 1099        /// * value: Amount to transfer.1100        #[weight = T::WeightInfo::transfer_from()]1101        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {11021103            let sender = ensure_signed(origin)?;1104            let mut appoved_transfer = false;11051106            // Check approve1107            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1108                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1109                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1110                if opt_item.is_some()1111                {1112                    appoved_transfer = true;1113                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1114                }1115            }11161117            // Transfer permissions check1118            let target_collection = <Collection<T>>::get(collection_id);1119                ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1120                Error::<T>::NoPermission);11211122            if target_collection.access == AccessMode::WhiteList {1123                Self::check_white_list(collection_id, &sender)?;1124                Self::check_white_list(collection_id, &recipient)?;1125            }11261127            // remove approve1128            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1129                .into_iter().filter(|i| i.approved != sender.clone()).collect();1130            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);113111321133            match target_collection.mode1134            {1135                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1136                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1137                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1138                _ => ()1139            };11401141            Ok(())1142        }11431144        ///1145        #[weight = 0]1146        pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11471148            // let no_perm_mes = "You do not have permissions to modify this collection";1149            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1150            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1151            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11521153            // // on_nft_received  call11541155            // Self::transfer(origin, collection_id, item_id, new_owner)?;11561157            Ok(())1158        }11591160        /// Set off-chain data schema.1161        /// 1162        /// # Permissions1163        /// 1164        /// * Collection Owner1165        /// * Collection Admin1166        /// 1167        /// # Arguments1168        /// 1169        /// * collection_id.1170        /// 1171        /// * schema: String representing the offchain data schema.1172        #[weight = T::WeightInfo::set_variable_meta_data()]1173        pub fn set_variable_meta_data (1174            origin,1175            collection_id: u64,1176            item_id: u64,1177            data: Vec<u8>1178        ) -> DispatchResult {1179            let sender = ensure_signed(origin)?;1180            1181            Self::collection_exists(collection_id)?;11821183            // Modify permissions check1184            let target_collection = <Collection<T>>::get(collection_id);1185            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1186                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1187                Error::<T>::NoPermission);11881189            Self::item_exists(collection_id, item_id, &target_collection.mode)?;11901191            match target_collection.mode1192            {1193                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1194                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1195                _ => ()1196            };11971198            Ok(())1199        }1200        12011202        /// Set off-chain data schema.1203        /// 1204        /// # Permissions1205        /// 1206        /// * Collection Owner1207        /// * Collection Admin1208        /// 1209        /// # Arguments1210        /// 1211        /// * collection_id.1212        /// 1213        /// * schema: String representing the offchain data schema.1214        #[weight = T::WeightInfo::set_offchain_schema()]1215        pub fn set_offchain_schema(1216            origin,1217            collection_id: u64,1218            schema: Vec<u8>1219        ) -> DispatchResult {1220            let sender = ensure_signed(origin)?;1221            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12221223            let mut target_collection = <Collection<T>>::get(collection_id);1224            target_collection.offchain_schema = schema;1225            <Collection<T>>::insert(collection_id, target_collection);12261227            Ok(())1228        }12291230        /// Set const on-chain data schema.1231        /// 1232        /// # Permissions1233        /// 1234        /// * Collection Owner1235        /// * Collection Admin1236        /// 1237        /// # Arguments1238        /// 1239        /// * collection_id.1240        /// 1241        /// * schema: String representing the const on-chain data schema.1242        #[weight = T::WeightInfo::set_const_on_chain_schema()]1243        pub fn set_const_on_chain_schema (1244            origin,1245            collection_id: u64,1246            schema: Vec<u8>1247        ) -> DispatchResult {1248            let sender = ensure_signed(origin)?;1249            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12501251            let mut target_collection = <Collection<T>>::get(collection_id);1252            target_collection.const_on_chain_schema = schema;1253            <Collection<T>>::insert(collection_id, target_collection);12541255            Ok(())1256        }12571258        /// Set variable on-chain data schema.1259        /// 1260        /// # Permissions1261        /// 1262        /// * Collection Owner1263        /// * Collection Admin1264        /// 1265        /// # Arguments1266        /// 1267        /// * collection_id.1268        /// 1269        /// * schema: String representing the variable on-chain data schema.1270        #[weight = T::WeightInfo::set_const_on_chain_schema()]1271        pub fn set_variable_on_chain_schema (1272            origin,1273            collection_id: u64,1274            schema: Vec<u8>1275        ) -> DispatchResult {1276            let sender = ensure_signed(origin)?;1277            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12781279            let mut target_collection = <Collection<T>>::get(collection_id);1280            target_collection.variable_on_chain_schema = schema;1281            <Collection<T>>::insert(collection_id, target_collection);12821283            Ok(())1284        }12851286        // Sudo permissions function1287        #[weight = 0]1288        pub fn set_chain_limits(1289            origin,1290            limits: ChainLimits1291        ) -> DispatchResult {1292            ensure_root(origin)?;1293            <ChainLimit>::put(limits);1294            Ok(())1295        }12961297        /// Enable smart contract self-sponsoring.1298        /// 1299        /// # Permissions1300        /// 1301        /// * Contract Owner1302        /// 1303        /// # Arguments1304        /// 1305        /// * contract address1306        /// * enable flag1307        /// 1308        #[weight = 0]1309        pub fn enable_contract_sponsoring(1310            origin,1311            contract_address: T::AccountId,1312            enable: bool1313        ) -> DispatchResult {1314            let sender = ensure_signed(origin)?;1315            let mut is_owner = false;1316            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1317                let owner = <ContractOwner<T>>::get(&contract_address);1318                is_owner = sender == owner;1319            }1320            ensure!(is_owner, Error::<T>::NoPermission);13211322            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1323            Ok(())1324        }13251326    }1327}13281329impl<T: Trait> Module<T> {13301331    fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {13321333        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1334            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1335            Self::check_white_list(collection_id, owner)?;1336            Self::check_white_list(collection_id, sender)?;1337        }13381339        Ok(())1340    }13411342    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1343        match target_collection.mode1344        {1345            CollectionMode::NFT => {1346                if let CreateItemData::NFT(data) = data {1347                    // check sizes1348                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");1349                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");1350                } else {1351                    fail!("Not NFT item data used to mint in NFT collection.");1352                }1353            },1354            CollectionMode::Fungible(_) => {1355                if let CreateItemData::Fungible(_) = data {1356                } else {1357                    fail!("Not Fungible item data used to mint in Fungible collection.");1358                }1359            },1360            CollectionMode::ReFungible(_) => {1361                if let CreateItemData::ReFungible(data) = data {13621363                    // check sizes1364                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");1365                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");1366                } else {1367                    fail!("Not Re Fungible item data used to mint in Re Fungible collection.");1368                }1369            },1370            _ => { fail!("Unexpected collection type."); }1371        };13721373        Ok(())1374    }13751376    fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1377        match data1378        {1379            CreateItemData::NFT(data) => {1380                let item = NftItemType {1381                    collection: collection_id,1382                    owner,1383                    const_data: data.const_data,1384                    variable_data: data.variable_data1385                };13861387                Self::add_nft_item(item)?;1388            },1389            CreateItemData::Fungible(_) => {1390                let item = FungibleItemType {1391                    collection: collection_id,1392                    owner,1393                    value: (10 as u128).pow(collection.decimal_points)1394                };13951396                Self::add_fungible_item(item)?;1397            },1398            CreateItemData::ReFungible(data) => {1399                let mut owner_list = Vec::new();1400                let value = (10 as u128).pow(collection.decimal_points);1401                owner_list.push(Ownership {owner: owner.clone(), fraction: value});14021403                let item = ReFungibleItemType {1404                    collection: collection_id,1405                    owner: owner_list,1406                    const_data: data.const_data,1407                    variable_data: data.variable_data1408                };14091410                Self::add_refungible_item(item)?;1411            }1412        };141314141415        // call event1416        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));14171418        Ok(())1419    }14201421    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1422        let current_index = <ItemListIndex>::get(item.collection)1423            .checked_add(1)1424            .ok_or(Error::<T>::NumOverflow)?;1425        let itemcopy = item.clone();1426        let owner = item.owner.clone();1427        let value = item.value as u64;14281429        Self::add_token_index(item.collection, current_index, owner.clone())?;14301431        <ItemListIndex>::insert(item.collection, current_index);1432        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14331434        // Add current block1435        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1436        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1437        1438        // Update balance1439        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1440            .checked_add(value)1441            .ok_or(Error::<T>::NumOverflow)?;1442        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14431444        Ok(())1445    }14461447    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1448        let current_index = <ItemListIndex>::get(item.collection)1449            .checked_add(1)1450            .ok_or(Error::<T>::NumOverflow)?;1451        let itemcopy = item.clone();14521453        let value = item.owner.first().unwrap().fraction as u64;1454        let owner = item.owner.first().unwrap().owner.clone();14551456        Self::add_token_index(item.collection, current_index, owner.clone())?;14571458        <ItemListIndex>::insert(item.collection, current_index);1459        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14601461        // Add current block1462        let block_number: T::BlockNumber = 0.into();1463        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);14641465        // Update balance1466        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1467            .checked_add(value)1468            .ok_or(Error::<T>::NumOverflow)?;1469        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14701471        Ok(())1472    }14731474    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1475        let current_index = <ItemListIndex>::get(item.collection)1476            .checked_add(1)1477            .ok_or(Error::<T>::NumOverflow)?;14781479        let item_owner = item.owner.clone();1480        let collection_id = item.collection.clone();1481        Self::add_token_index(collection_id, current_index, item.owner.clone())?;14821483        <ItemListIndex>::insert(collection_id, current_index);1484        <NftItemList<T>>::insert(collection_id, current_index, item);14851486        // Add current block1487        let block_number: T::BlockNumber = 0.into();1488        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);14891490        // Update balance1491        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1492            .checked_add(1)1493            .ok_or(Error::<T>::NumOverflow)?;1494        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);14951496        Ok(())1497    }14981499    fn burn_refungible_item(1500        collection_id: u64,1501        item_id: u64,1502        owner: T::AccountId,1503    ) -> DispatchResult {1504        ensure!(1505            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1506            Error::<T>::TokenNotFound1507        );1508        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1509        let item = collection1510            .owner1511            .iter()1512            .filter(|&i| i.owner == owner)1513            .next()1514            .unwrap();1515        Self::remove_token_index(collection_id, item_id, owner.clone())?;15161517        // remove approve list1518        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));15191520        // update balance1521        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1522            .checked_sub(item.fraction as u64)1523            .ok_or(Error::<T>::NumOverflow)?;1524        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15251526        <ReFungibleItemList<T>>::remove(collection_id, item_id);15271528        Ok(())1529    }15301531    fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1532        ensure!(1533            <NftItemList<T>>::contains_key(collection_id, item_id),1534            Error::<T>::TokenNotFound1535        );1536        let item = <NftItemList<T>>::get(collection_id, item_id);1537        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15381539        // remove approve list1540        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15411542        // update balance1543        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1544            .checked_sub(1)1545            .ok_or(Error::<T>::NumOverflow)?;1546        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1547        <NftItemList<T>>::remove(collection_id, item_id);15481549        Ok(())1550    }15511552    fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1553        ensure!(1554            <FungibleItemList<T>>::contains_key(collection_id, item_id),1555            Error::<T>::TokenNotFound1556        );1557        let item = <FungibleItemList<T>>::get(collection_id, item_id);1558        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15591560        // remove approve list1561        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15621563        // update balance1564        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1565            .checked_sub(item.value as u64)1566            .ok_or(Error::<T>::NumOverflow)?;1567        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15681569        <FungibleItemList<T>>::remove(collection_id, item_id);15701571        Ok(())1572    }15731574    fn collection_exists(collection_id: u64) -> DispatchResult {1575        ensure!(1576            <Collection<T>>::contains_key(collection_id),1577            Error::<T>::CollectionNotFound1578        );1579        Ok(())1580    }15811582    fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1583        Self::collection_exists(collection_id)?;15841585        let target_collection = <Collection<T>>::get(collection_id);1586        ensure!(1587            subject == target_collection.owner,1588            Error::<T>::NoPermission1589        );15901591        Ok(())1592    }15931594    fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1595        let target_collection = <Collection<T>>::get(collection_id);1596        let mut result: bool = subject == target_collection.owner;1597        let exists = <AdminList<T>>::contains_key(collection_id);15981599        if !result & exists {1600            if <AdminList<T>>::get(collection_id).contains(&subject) {1601                result = true1602            }1603        }16041605        result1606    }16071608    fn check_owner_or_admin_permissions(1609        collection_id: u64,1610        subject: T::AccountId,1611    ) -> DispatchResult {1612        Self::collection_exists(collection_id)?;1613        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());16141615        ensure!(1616            result,1617            Error::<T>::NoPermission1618        );1619        Ok(())1620    }16211622    fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1623        let target_collection = <Collection<T>>::get(collection_id);16241625        match target_collection.mode {1626            CollectionMode::NFT => {1627                <NftItemList<T>>::get(collection_id, item_id).owner == subject1628            }1629            CollectionMode::Fungible(_) => {1630                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1631            }1632            CollectionMode::ReFungible(_) => {1633                <ReFungibleItemList<T>>::get(collection_id, item_id)1634                    .owner1635                    .iter()1636                    .any(|i| i.owner == subject)1637            }1638            CollectionMode::Invalid => false,1639        }1640    }16411642    fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1643        let mes = Error::<T>::AddresNotInWhiteList;1644        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1645        let wl = <WhiteList<T>>::get(collection_id);1646        ensure!(wl.contains(address), mes);16471648        Ok(())1649    }16501651    fn transfer_fungible(1652        collection_id: u64,1653        item_id: u64,1654        value: u64,1655        owner: T::AccountId,1656        new_owner: T::AccountId,1657    ) -> DispatchResult {1658        ensure!(1659            <FungibleItemList<T>>::contains_key(collection_id, item_id),1660            Error::<T>::TokenNotFound1661        );16621663        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1664        let amount = full_item.value;16651666        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);16671668        // update balance1669        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1670            .checked_sub(value)1671            .ok_or(Error::<T>::NumOverflow)?;1672        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);16731674        let mut new_owner_account_id = 0;1675        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1676        if new_owner_items.len() > 0 {1677            new_owner_account_id = new_owner_items[0];1678        }16791680        let val64 = value.into();16811682        // transfer1683        if amount == val64 && new_owner_account_id == 0 {1684            // change owner1685            // new owner do not have account1686            let mut new_full_item = full_item.clone();1687            new_full_item.owner = new_owner.clone();1688            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);16891690            // update balance1691            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1692                .checked_add(value)1693                .ok_or(Error::<T>::NumOverflow)?;1694            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16951696            // update index collection1697            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1698        } else {1699            let mut new_full_item = full_item.clone();1700            new_full_item.value -= val64;17011702            // separate amount1703            if new_owner_account_id > 0 {1704                // new owner has account1705                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1706                item.value += val64;17071708                // update balance1709                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1710                    .checked_add(value)1711                    .ok_or(Error::<T>::NumOverflow)?;1712                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17131714                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1715            } else {1716                // new owner do not have account1717                let item = FungibleItemType {1718                    collection: collection_id,1719                    owner: new_owner.clone(),1720                    value: val64,1721                };17221723                Self::add_fungible_item(item)?;1724            }17251726            if amount == val64 {1727                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;17281729                // remove approve list1730                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1731                <FungibleItemList<T>>::remove(collection_id, item_id);1732            }17331734            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1735        }17361737        Ok(())1738    }17391740    fn transfer_refungible(1741        collection_id: u64,1742        item_id: u64,1743        value: u64,1744        owner: T::AccountId,1745        new_owner: T::AccountId,1746    ) -> DispatchResult {1747        ensure!(1748            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1749            Error::<T>::TokenNotFound1750        );17511752        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1753        let item = full_item1754            .owner1755            .iter()1756            .filter(|i| i.owner == owner)1757            .next()1758            .ok_or(Error::<T>::NumOverflow)?;1759        let amount = item.fraction;17601761        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);17621763        // update balance1764        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1765            .checked_sub(value)1766            .ok_or(Error::<T>::NumOverflow)?;1767        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);17681769        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1770            .checked_add(value)1771            .ok_or(Error::<T>::NumOverflow)?;1772        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17731774        let old_owner = item.owner.clone();1775        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1776        let val64 = value.into();17771778        // transfer1779        if amount == val64 && !new_owner_has_account {1780            // change owner1781            // new owner do not have account1782            let mut new_full_item = full_item.clone();1783            new_full_item1784                .owner1785                .iter_mut()1786                .find(|i| i.owner == owner)1787                .unwrap()1788                .owner = new_owner.clone();1789            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17901791            // update index collection1792            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1793        } else {1794            let mut new_full_item = full_item.clone();1795            new_full_item1796                .owner1797                .iter_mut()1798                .find(|i| i.owner == owner)1799                .unwrap()1800                .fraction -= val64;18011802            // separate amount1803            if new_owner_has_account {1804                // new owner has account1805                new_full_item1806                    .owner1807                    .iter_mut()1808                    .find(|i| i.owner == new_owner)1809                    .unwrap()1810                    .fraction += val64;1811            } else {1812                // new owner do not have account1813                new_full_item.owner.push(Ownership {1814                    owner: new_owner.clone(),1815                    fraction: val64,1816                });1817                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1818            }18191820            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1821        }18221823        Ok(())1824    }18251826    fn transfer_nft(1827        collection_id: u64,1828        item_id: u64,1829        sender: T::AccountId,1830        new_owner: T::AccountId,1831    ) -> DispatchResult {1832        ensure!(1833            <NftItemList<T>>::contains_key(collection_id, item_id),1834            Error::<T>::TokenNotFound1835        );18361837        let mut item = <NftItemList<T>>::get(collection_id, item_id);18381839        ensure!(1840            sender == item.owner,1841            Error::<T>::MustBeTokenOwner1842        );18431844        // update balance1845        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1846            .checked_sub(1)1847            .ok_or(Error::<T>::NumOverflow)?;1848        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18491850        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1851            .checked_add(1)1852            .ok_or(Error::<T>::NumOverflow)?;1853        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18541855        // change owner1856        let old_owner = item.owner.clone();1857        item.owner = new_owner.clone();1858        <NftItemList<T>>::insert(collection_id, item_id, item);18591860        // update index collection1861        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;18621863        // reset approved list1864        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1865        Ok(())1866    }1867    1868    fn item_exists(1869        collection_id: u64,1870        item_id: u64,1871        mode: &CollectionMode1872    ) -> DispatchResult {1873        match mode {1874            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1875            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1876            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1877            _ => ()1878        };1879        1880        Ok(())1881    }18821883    fn set_re_fungible_variable_data(1884        collection_id: u64,1885        item_id: u64,1886        data: Vec<u8>1887    ) -> DispatchResult {1888        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);18891890        item.variable_data = data;18911892        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);18931894        Ok(())1895    }18961897    fn set_nft_variable_data(1898        collection_id: u64,1899        item_id: u64,1900        data: Vec<u8>1901    ) -> DispatchResult {1902        let mut item = <NftItemList<T>>::get(collection_id, item_id);1903        1904        item.variable_data = data;19051906        <NftItemList<T>>::insert(collection_id, item_id, item);1907        1908        Ok(())1909    }19101911    fn init_collection(item: &CollectionType<T::AccountId>) {1912        // check params1913        assert!(1914            item.decimal_points <= 4,1915            "decimal_points parameter must be lower than 4"1916        );1917        assert!(1918            item.name.len() <= 64,1919            "Collection name can not be longer than 63 char"1920        );1921        assert!(1922            item.name.len() <= 256,1923            "Collection description can not be longer than 255 char"1924        );1925        assert!(1926            item.token_prefix.len() <= 16,1927            "Token prefix can not be longer than 15 char"1928        );19291930        // Generate next collection ID1931        let next_id = CreatedCollectionCount::get()1932            .checked_add(1)1933            .unwrap();19341935        CreatedCollectionCount::put(next_id);1936    }19371938    fn init_nft_token(item: &NftItemType<T::AccountId>) {1939        let current_index = <ItemListIndex>::get(item.collection)1940            .checked_add(1)1941            .unwrap();19421943        let item_owner = item.owner.clone();1944        let collection_id = item.collection.clone();1945        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();19461947        <ItemListIndex>::insert(collection_id, current_index);19481949        // Update balance1950        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1951            .checked_add(1)1952            .unwrap();1953        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1954    }19551956    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1957        let current_index = <ItemListIndex>::get(item.collection)1958            .checked_add(1)1959            .unwrap();1960        let owner = item.owner.clone();1961        let value = item.value as u64;19621963        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();19641965        <ItemListIndex>::insert(item.collection, current_index);19661967        // Update balance1968        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1969            .checked_add(value)1970            .unwrap();1971        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1972    }19731974    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1975        let current_index = <ItemListIndex>::get(item.collection)1976            .checked_add(1)1977            .unwrap();19781979        let value = item.owner.first().unwrap().fraction as u64;1980        let owner = item.owner.first().unwrap().owner.clone();19811982        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();19831984        <ItemListIndex>::insert(item.collection, current_index);19851986        // Update balance1987        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1988            .checked_add(value)1989            .unwrap();1990        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1991    }19921993    fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {19941995        // add to account limit1996        if <AccountItemCount<T>>::contains_key(owner.clone()) {19971998            // bound Owned tokens by a single address1999            let count = <AccountItemCount<T>>::get(owner.clone());2000            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);20012002            <AccountItemCount<T>>::insert(owner.clone(), count2003                .checked_add(1)2004                .ok_or(Error::<T>::NumOverflow)?);2005        }2006        else {2007            <AccountItemCount<T>>::insert(owner.clone(), 1);2008        }20092010        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2011        if list_exists {2012            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2013            let item_contains = list.contains(&item_index.clone());20142015            if !item_contains {2016                list.push(item_index.clone());2017            }20182019            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2020        } else {2021            let mut itm = Vec::new();2022            itm.push(item_index.clone());2023            <AddressTokens<T>>::insert(collection_id, owner, itm);2024            2025        }20262027        Ok(())2028    }20292030    fn remove_token_index(2031        collection_id: u64,2032        item_index: u64,2033        owner: T::AccountId,2034    ) -> DispatchResult {20352036        // update counter2037        <AccountItemCount<T>>::insert(owner.clone(), 2038            <AccountItemCount<T>>::get(owner.clone())2039            .checked_sub(1)2040            .ok_or(Error::<T>::NumOverflow)?);204120422043        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2044        if list_exists {2045            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2046            let item_contains = list.contains(&item_index.clone());20472048            if item_contains {2049                list.retain(|&item| item != item_index);2050                <AddressTokens<T>>::insert(collection_id, owner, list);2051            }2052        }20532054        Ok(())2055    }20562057    fn move_token_index(2058        collection_id: u64,2059        item_index: u64,2060        old_owner: T::AccountId,2061        new_owner: T::AccountId,2062    ) -> DispatchResult {2063        Self::remove_token_index(collection_id, item_index, old_owner)?;2064        Self::add_token_index(collection_id, item_index, new_owner)?;20652066        Ok(())2067    }2068}20692070////////////////////////////////////////////////////////////////////////////////////////////////////2071// Economic models2072// #region20732074/// Fee multiplier.2075pub type Multiplier = FixedU128;20762077type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2078    <T as system::Trait>::AccountId,2079>>::Balance;2080type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2081    <T as system::Trait>::AccountId,2082>>::NegativeImbalance;20832084/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2085/// in the queue.2086#[derive(Encode, Decode, Clone, Eq, PartialEq)]2087pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2088    #[codec(compact)] BalanceOf<T>2089);20902091impl<T: Trait + Send + Sync> sp_std::fmt::Debug2092    for ChargeTransactionPayment<T>2093{2094    #[cfg(feature = "std")]2095    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2096        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2097    }2098    #[cfg(not(feature = "std"))]2099    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2100        Ok(())2101    }2102}21032104impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2105where2106    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2107    BalanceOf<T>: Send + Sync + FixedPointOperand,2108{2109    /// utility constructor. Used only in client/factory code.2110    pub fn from(fee: BalanceOf<T>) -> Self {2111        Self(fee)2112    }21132114    pub fn traditional_fee(2115        len: usize,2116        info: &DispatchInfoOf<T::Call>,2117        tip: BalanceOf<T>,2118    ) -> BalanceOf<T>2119    where2120        T::Call: Dispatchable<Info = DispatchInfo>,2121    {2122        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2123    }21242125    fn withdraw_fee(2126        &self,2127        who: &T::AccountId,2128        call: &T::Call,2129        info: &DispatchInfoOf<T::Call>,2130        len: usize,2131    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2132        let tip = self.0;21332134        // Set fee based on call type. Creating collection costs 1 Unique.2135        // All other transactions have traditional fees so far2136        // let fee = match call.is_sub_type() {2137        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2138        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2139        //                                                 // _ => <BalanceOf<T>>::from(100)2140        // };2141        let fee = Self::traditional_fee(len, info, tip);21422143        // Determine who is paying transaction fee based on ecnomic model2144        // Parse call to extract collection ID and access collection sponsor2145        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2146            Some(Call::create_item(collection_id, _properties, _owner)) => {2147                <Collection<T>>::get(collection_id).sponsor2148            }2149            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2150                let _collection_mode = <Collection<T>>::get(collection_id).mode;21512152                // sponsor timeout2153                let sponsor_transfer = match _collection_mode {2154                    CollectionMode::NFT => {2155                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2156                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2157                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2158                        if block_number >= limit_time {2159                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2160                            true2161                        }2162                        else {2163                            false2164                        }2165                    }2166                    CollectionMode::Fungible(_) => {2167                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2168                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2169                        if basket.iter().any(|i| i.address == _new_owner.clone())2170                        {2171                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2172                            let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2173                            if block_number >= limit_time {2174                                basket.retain(|x| x.address == item.address);2175                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2176                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2177                                true2178                            }2179                            else {2180                                false2181                            }2182                        }2183                        else {2184                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2185                            true2186                        }2187                    }2188                    CollectionMode::ReFungible(_) => {2189                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2190                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2191                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2192                        if block_number >= limit_time {2193                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2194                            true2195                        } else {2196                            false2197                        }2198                    }2199                    _ => {2200                        false2201                    },2202                };22032204                if !sponsor_transfer {2205                    T::AccountId::default()2206                } else {2207                    <Collection<T>>::get(collection_id).sponsor2208                }2209            }22102211            _ => T::AccountId::default(),2212        };22132214        // Sponsor smart contracts2215        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {22162217            // On instantiation: set the contract owner2218            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {22192220                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2221                    code_hash,2222                    &data,2223                    &who,2224                );2225                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());22262227                T::AccountId::default()2228            },22292230            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2231            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {22322233                let mut sp = T::AccountId::default();2234                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());2235                if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2236                    if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2237                        sp = called_contract;2238                    }2239                }22402241                sp2242            },22432244            _ => sponsor,2245        };22462247        let mut who_pays_fee: T::AccountId = sponsor.clone();2248        if sponsor == T::AccountId::default() {2249            who_pays_fee = who.clone();2250        }22512252        // Only mess with balances if fee is not zero.2253        if fee.is_zero() {2254            return Ok((fee, None));2255        }22562257        match <T as transaction_payment::Trait>::Currency::withdraw(2258            &who_pays_fee,2259            fee,2260            if tip.is_zero() {2261                WithdrawReason::TransactionPayment.into()2262            } else {2263                WithdrawReason::TransactionPayment | WithdrawReason::Tip2264            },2265            ExistenceRequirement::KeepAlive,2266        ) {2267            Ok(imbalance) => Ok((fee, Some(imbalance))),2268            Err(_) => Err(InvalidTransaction::Payment.into()),2269        }2270    }2271}227222732274impl<T: Trait + Send + Sync> SignedExtension2275    for ChargeTransactionPayment<T>2276where2277    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2278    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2279{2280    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2281    type AccountId = T::AccountId;2282    type Call = T::Call;2283    type AdditionalSigned = ();2284    type Pre = (2285        BalanceOf<T>,2286        Self::AccountId,2287        Option<NegativeImbalanceOf<T>>,2288        BalanceOf<T>,2289    );2290    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2291        Ok(())2292    }22932294    fn validate(2295        &self,2296        _who: &Self::AccountId,2297        _call: &Self::Call,2298        _info: &DispatchInfoOf<Self::Call>,2299        _len: usize,2300    ) -> TransactionValidity {2301        Ok(ValidTransaction::default())2302    }23032304    fn pre_dispatch(2305        self,2306        who: &Self::AccountId,2307        call: &Self::Call,2308        info: &DispatchInfoOf<Self::Call>,2309        len: usize,2310    ) -> Result<Self::Pre, TransactionValidityError> {2311        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2312        Ok((self.0, who.clone(), imbalance, fee))2313    }23142315    fn post_dispatch(2316        pre: Self::Pre,2317        info: &DispatchInfoOf<Self::Call>,2318        post_info: &PostDispatchInfoOf<Self::Call>,2319        len: usize,2320        _result: &DispatchResult,2321    ) -> Result<(), TransactionValidityError> {2322        let (tip, who, imbalance, fee) = pre;2323        if let Some(payed) = imbalance {2324            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2325                len as u32, info, post_info, tip,2326            );2327            let refund = fee.saturating_sub(actual_fee);2328            let actual_payment =2329                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2330                    &who, refund,2331                ) {2332                    Ok(refund_imbalance) => {2333                        // The refund cannot be larger than the up front payed max weight.2334                        // `PostDispatchInfo::calc_unspent` guards against such a case.2335                        match payed.offset(refund_imbalance) {2336                            Ok(actual_payment) => actual_payment,2337                            Err(_) => return Err(InvalidTransaction::Payment.into()),2338                        }2339                    }2340                    // We do not recreate the account using the refund. The up front payment2341                    // is gone in that case.2342                    Err(_) => payed,2343                };2344            let imbalances = actual_payment.split(tip);2345            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2346                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2347            );2348        }2349        Ok(())2350    }2351}23522353// #endregion
after · pallets/nft/src/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]23#[cfg(feature = "std")]4pub use std::*;56#[cfg(feature = "std")]7pub use serde::*;89use codec::{Decode, Encode};10pub use frame_support::{11    construct_runtime, decl_event, decl_module, decl_storage, decl_error,12    dispatch::DispatchResult,13    ensure, fail, parameter_types,14    traits::{15        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,16        Randomness, WithdrawReason,17    },18    weights::{19        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},20        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,21        WeightToFeePolynomial,22    },23    IsSubType, StorageValue,24};2526use frame_system::{self as system, ensure_signed, ensure_root};27use sp_runtime::sp_std::prelude::Vec;28use sp_runtime::{29    traits::{30        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SignedExtension, Zero,31    },32    transaction_validity::{33        InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,34    },35    FixedPointOperand, FixedU128,36};37use pallet_contracts::ContractAddressFor;38use sp_runtime::traits::StaticLookup;3940#[cfg(test)]41mod mock;4243#[cfg(test)]44mod tests;4546mod default_weights;4748// Structs49// #region5051#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]52#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]53pub enum CollectionMode {54    Invalid,55    NFT,56    // decimal points57    Fungible(u32),58    // decimal points59    ReFungible(u32),60}6162impl Into<u8> for CollectionMode {63    fn into(self) -> u8 {64        match self {65            CollectionMode::Invalid => 0,66            CollectionMode::NFT => 1,67            CollectionMode::Fungible(_) => 2,68            CollectionMode::ReFungible(_) => 3,69        }70    }71}7273#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]74#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]75pub enum AccessMode {76    Normal,77    WhiteList,78}79impl Default for AccessMode {80    fn default() -> Self {81        Self::Normal82    }83}8485impl Default for CollectionMode {86    fn default() -> Self {87        Self::Invalid88    }89}9091#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]92#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]93pub struct Ownership<AccountId> {94    pub owner: AccountId,95    pub fraction: u128,96}9798#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]99#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]100pub struct CollectionType<AccountId> {101    pub owner: AccountId,102    pub mode: CollectionMode,103    pub access: AccessMode,104    pub decimal_points: u32,105    pub name: Vec<u16>,        // 64 include null escape char106    pub description: Vec<u16>, // 256 include null escape char107    pub token_prefix: Vec<u8>, // 16 include null escape char108    pub mint_mode: bool,109    pub offchain_schema: Vec<u8>,110    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender111    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship112    pub variable_on_chain_schema: Vec<u8>, //113    pub const_on_chain_schema: Vec<u8>, //114}115116#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]117#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]118pub struct NftItemType<AccountId> {119    pub collection: u64,120    pub owner: AccountId,121    pub const_data: Vec<u8>,122    pub variable_data: Vec<u8>,123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct FungibleItemType<AccountId> {128    pub collection: u64,129    pub owner: AccountId,130    pub value: u128,131}132133#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]134#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]135pub struct ReFungibleItemType<AccountId> {136    pub collection: u64,137    pub owner: Vec<Ownership<AccountId>>,138    pub const_data: Vec<u8>,139    pub variable_data: Vec<u8>,140}141142#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]143#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]144pub struct ApprovePermissions<AccountId> {145    pub approved: AccountId,146    pub amount: u64,147}148149#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]150#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]151pub struct VestingItem<AccountId, Moment> {152    pub sender: AccountId,153    pub recipient: AccountId,154    pub collection_id: u64,155    pub item_id: u64,156    pub amount: u64,157    pub vesting_date: Moment,158}159160#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]161#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]162pub struct BasketItem<AccountId, BlockNumber> {163    pub address: AccountId,164    pub start_block: BlockNumber,165}166167#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]168#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169pub struct ChainLimits {170    pub collection_numbers_limit: u64,171    pub account_token_ownership_limit: u64,172    pub collections_admins_limit: u64,173    pub custom_data_limit: u32,174175    // Timeouts for item types in passed blocks176    pub nft_sponsor_transfer_timeout: u32,177    pub fungible_sponsor_transfer_timeout: u32,178    pub refungible_sponsor_transfer_timeout: u32,179}180181pub trait WeightInfo {182	fn create_collection() -> Weight;183	fn destroy_collection() -> Weight;184	fn add_to_white_list() -> Weight;185	fn remove_from_white_list() -> Weight;186    fn set_public_access_mode() -> Weight;187    fn set_mint_permission() -> Weight;188    fn change_collection_owner() -> Weight;189    fn add_collection_admin() -> Weight;190    fn remove_collection_admin() -> Weight;191    fn set_collection_sponsor() -> Weight;192    fn confirm_sponsorship() -> Weight;193    fn remove_collection_sponsor() -> Weight;194    fn create_item(s: usize) -> Weight;195    fn burn_item() -> Weight;196    fn transfer() -> Weight;197    fn approve() -> Weight;198    fn transfer_from() -> Weight;199    fn set_offchain_schema() -> Weight;200    fn set_const_on_chain_schema() -> Weight;201    fn set_variable_on_chain_schema() -> Weight;202    fn set_variable_meta_data() -> Weight;203    // fn enable_contract_sponsoring() -> Weight;204}205206#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]207#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]208pub struct CreateNftData {209    pub const_data: Vec<u8>,210    pub variable_data: Vec<u8>,211}212213#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]214#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]215pub struct CreateFungibleData {216}217218#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]219#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]220pub struct CreateReFungibleData {221    pub const_data: Vec<u8>,222    pub variable_data: Vec<u8>,223}224225#[derive(Encode, Decode, Debug, Clone, PartialEq)]226#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]227pub enum CreateItemData {228    NFT(CreateNftData),229    Fungible(CreateFungibleData),230    ReFungible(CreateReFungibleData)231}232233impl CreateItemData {234    pub fn len(&self) -> usize {235        let len = match self {236            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),237            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),238            _ => 0239        };240        241        return len;242    }243}244245impl From<CreateNftData> for CreateItemData {246    fn from(item: CreateNftData) -> Self {247        CreateItemData::NFT(item)248    }249}250251impl From<CreateReFungibleData> for CreateItemData {252    fn from(item: CreateReFungibleData) -> Self {253        CreateItemData::ReFungible(item)254    }255}256257impl From<CreateFungibleData> for CreateItemData {258    fn from(item: CreateFungibleData) -> Self {259        CreateItemData::Fungible(item)260    }261}262263264decl_error! {265	/// Error for non-fungible-token module.266	pub enum Error for Module<T: Trait> {267        /// Total collections bound exceeded268        TotalCollectionsLimitExceeded,269		/// Decimal_points parameter must be lower than 4270        CollectionDecimalPointLimitExceeded, 271        /// Collection name can not be longer than 63 char272        CollectionNameLimitExceeded, 273        /// Collection description can not be longer than 255 char274        CollectionDescriptionLimitExceeded, 275        /// Token prefix can not be longer than 15 char276        CollectionTokenPrefixLimitExceeded,277        /// This collection does not exist278        CollectionNotFound,279        /// Item not exists280        TokenNotFound,281        /// Arithmetic calculation overflow282        NumOverflow,       283        /// Account already has admin role284        AlreadyAdmin,  285        /// You do not own this collection286        NoPermission,287        /// This address is not set as sponsor, use setCollectionSponsor first288        ConfirmUnsetSponsorFail,289        /// Collection is not in mint mode290        PublicMintingNotAllowed,291        /// Sender parameter and item owner must be equal292        MustBeTokenOwner,293        /// Item balance not enouth294        TokenValueTooLow,295        /// Size of item is too large296        NftSizeLimitExceeded,297        /// Size of item must be 0 with fungible type298        FungibleUnexpectedParam,299        /// No approve found300        ApproveNotFound,301        /// Requested value more than approved302        TokenValueNotEnough,303        /// Only approved addresses can call this method304        ApproveRequired,305        /// Address is not in white list306        AddresNotInWhiteList,307        /// Number of collection admins bound exceeded308        CollectionAdminsLimitExceeded,309        /// Owned tokens by a single address bound exceeded310        AddressOwnershipLimitExceeded,311        /// Length of items properties must be greater than 0312        EmptyArgument,313	}314}315316pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {317    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;318319    /// Weight information for extrinsics in this pallet.320	type WeightInfo: WeightInfo;321}322323#[cfg(feature = "runtime-benchmarks")]324mod benchmarking;325326// #endregion327328decl_storage! {329    trait Store for Module<T: Trait> as Nft {330331        // Private members332        NextCollectionID: u64;333        CreatedCollectionCount: u64;334        ChainVersion: u64;335        ItemListIndex: map hasher(blake2_128_concat) u64 => u64;336337        // Chain limits struct338        pub ChainLimit get(fn chain_limit) config(): ChainLimits;339340        // Bound counters341        CollectionCount: u64;342        pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;343344        // Basic collections345        pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;346        pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;347        pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;348349        /// Balance owner per collection map350        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;351352        /// second parameter: item id + owner account id353        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;354355        /// Item collections356        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;357        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;358        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;359360        /// Index list361        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;362363        /// Tokens transfer baskets364        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;365        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => Vec<BasketItem<T::AccountId, T::BlockNumber>>;366        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;367368        // Contract Sponsorship and Ownership369        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;370        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;371        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;372        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;373    }374    add_extra_genesis {375        build(|config: &GenesisConfig<T>| {376            // Modification of storage377            for (_num, _c) in &config.collection {378                <Module<T>>::init_collection(_c);379            }380381            for (_num, _q, _i) in &config.nft_item_id {382                <Module<T>>::init_nft_token(_i);383            }384385            for (_num, _q, _i) in &config.fungible_item_id {386                <Module<T>>::init_fungible_token(_i);387            }388389            for (_num, _q, _i) in &config.refungible_item_id {390                <Module<T>>::init_refungible_token(_i);391            }392        })393    }394}395396decl_event!(397    pub enum Event<T>398    where399        AccountId = <T as system::Trait>::AccountId,400    {401        /// New collection was created402        /// 403        /// # Arguments404        /// 405        /// * collection_id: Globally unique identifier of newly created collection.406        /// 407        /// * mode: [CollectionMode] converted into u8.408        /// 409        /// * account_id: Collection owner.410        Created(u64, u8, AccountId),411412        /// New item was created.413        /// 414        /// # Arguments415        /// 416        /// * collection_id: Id of the collection where item was created.417        /// 418        /// * item_id: Id of an item. Unique within the collection.419        ItemCreated(u64, u64),420421        /// Collection item was burned.422        /// 423        /// # Arguments424        /// 425        /// collection_id.426        /// 427        /// item_id: Identifier of burned NFT.428        ItemDestroyed(u64, u64),429    }430);431432decl_module! {433    pub struct Module<T: Trait> for enum Call where origin: T::Origin {434435        fn deposit_event() = default;436        type Error = Error<T>;437438        fn on_initialize(now: T::BlockNumber) -> Weight {439440            if ChainVersion::get() < 2441            {442                let value = NextCollectionID::get();443                CreatedCollectionCount::put(value);444                ChainVersion::put(2);445            }446447            0448        }449450        /// 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.451        /// 452        /// # Permissions453        /// 454        /// * Anyone.455        /// 456        /// # Arguments457        /// 458        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.459        /// 460        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.461        /// 462        /// * token_prefix: UTF-8 string with token prefix.463        /// 464        /// * mode: [CollectionMode] collection type and type dependent data.465        // returns collection ID466        #[weight = T::WeightInfo::create_collection()]467        pub fn create_collection(origin,468                                 collection_name: Vec<u16>,469                                 collection_description: Vec<u16>,470                                 token_prefix: Vec<u8>,471                                 mode: CollectionMode) -> DispatchResult {472473            // Anyone can create a collection474            let who = ensure_signed(origin)?;475476            let decimal_points = match mode {477                CollectionMode::Fungible(points) => points,478                CollectionMode::ReFungible(points) => points,479                _ => 0480            };481482            // bound Total number of collections483            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);484485            // check params486            ensure!(decimal_points <= 4, Error::<T>::CollectionDecimalPointLimitExceeded);487488            let mut name = collection_name.to_vec();489            name.push(0);490            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);491492            let mut description = collection_description.to_vec();493            description.push(0);494            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);495496            let mut prefix = token_prefix.to_vec();497            prefix.push(0);498            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);499500            // Generate next collection ID501            let next_id = CreatedCollectionCount::get()502                .checked_add(1)503                .ok_or(Error::<T>::NumOverflow)?;504505            // bound counter506            let total = CollectionCount::get()507                .checked_add(1)508                .ok_or(Error::<T>::NumOverflow)?;509510            CreatedCollectionCount::put(next_id);511            CollectionCount::put(total);512513            // Create new collection514            let new_collection = CollectionType {515                owner: who.clone(),516                name: name,517                mode: mode.clone(),518                mint_mode: false,519                access: AccessMode::Normal,520                description: description,521                decimal_points: decimal_points,522                token_prefix: prefix,523                offchain_schema: Vec::new(),524                sponsor: T::AccountId::default(),525                unconfirmed_sponsor: T::AccountId::default(),526                variable_on_chain_schema: Vec::new(),527                const_on_chain_schema: Vec::new(),528            };529530            // Add new collection to map531            <Collection<T>>::insert(next_id, new_collection);532533            // call event534            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));535536            Ok(())537        }538539        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.540        /// 541        /// # Permissions542        /// 543        /// * Collection Owner.544        /// 545        /// # Arguments546        /// 547        /// * collection_id: collection to destroy.548        #[weight = T::WeightInfo::destroy_collection()]549        pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {550551            let sender = ensure_signed(origin)?;552            Self::check_owner_permissions(collection_id, sender)?;553554            <AddressTokens<T>>::remove_prefix(collection_id);555            <ApprovedList<T>>::remove_prefix(collection_id);556            <Balance<T>>::remove_prefix(collection_id);557            <ItemListIndex>::remove(collection_id);558            <AdminList<T>>::remove(collection_id);559            <Collection<T>>::remove(collection_id);560            <WhiteList<T>>::remove(collection_id);561562            <NftItemList<T>>::remove_prefix(collection_id);563            <FungibleItemList<T>>::remove_prefix(collection_id);564            <ReFungibleItemList<T>>::remove_prefix(collection_id);565566            <NftTransferBasket<T>>::remove_prefix(collection_id);567            <FungibleTransferBasket<T>>::remove_prefix(collection_id);568            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);569570            if CollectionCount::get() > 0571            {572                // bound couter573                let total = CollectionCount::get()574                    .checked_sub(1)575                    .ok_or(Error::<T>::NumOverflow)?;576577                CollectionCount::put(total);578            }579580            Ok(())581        }582583        /// Add an address to white list.584        /// 585        /// # Permissions586        /// 587        /// * Collection Owner588        /// * Collection Admin589        /// 590        /// # Arguments591        /// 592        /// * collection_id.593        /// 594        /// * address.595        #[weight = T::WeightInfo::add_to_white_list()]596        pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{597598            let sender = ensure_signed(origin)?;599            Self::check_owner_or_admin_permissions(collection_id, sender)?;600601            let mut white_list_collection: Vec<T::AccountId>;602            if <WhiteList<T>>::contains_key(collection_id) {603                white_list_collection = <WhiteList<T>>::get(collection_id);604                if !white_list_collection.contains(&address.clone())605                {606                    white_list_collection.push(address.clone());607                }608            }609            else {610                white_list_collection = Vec::new();611                white_list_collection.push(address.clone());612            }613614            <WhiteList<T>>::insert(collection_id, white_list_collection);615            Ok(())616        }617618        /// Remove an address from white list.619        /// 620        /// # Permissions621        /// 622        /// * Collection Owner623        /// * Collection Admin624        /// 625        /// # Arguments626        /// 627        /// * collection_id.628        /// 629        /// * address.630        #[weight = T::WeightInfo::remove_from_white_list()]631        pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{632633            let sender = ensure_signed(origin)?;634            Self::check_owner_or_admin_permissions(collection_id, sender)?;635636            if <WhiteList<T>>::contains_key(collection_id) {637                let mut white_list_collection = <WhiteList<T>>::get(collection_id);638                if white_list_collection.contains(&address.clone())639                {640                    white_list_collection.retain(|i| *i != address.clone());641                    <WhiteList<T>>::insert(collection_id, white_list_collection);642                }643            }644645            Ok(())646        }647648        /// Toggle between normal and white list access for the methods with access for `Anyone`.649        /// 650        /// # Permissions651        /// 652        /// * Collection Owner.653        /// 654        /// # Arguments655        /// 656        /// * collection_id.657        /// 658        /// * mode: [AccessMode]659        #[weight = T::WeightInfo::set_public_access_mode()]660        pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult661        {662            let sender = ensure_signed(origin)?;663664            Self::check_owner_permissions(collection_id, sender)?;665            let mut target_collection = <Collection<T>>::get(collection_id);666            target_collection.access = mode;667            <Collection<T>>::insert(collection_id, target_collection);668669            Ok(())670        }671672        /// Allows Anyone to create tokens if:673        /// * White List is enabled, and674        /// * Address is added to white list, and675        /// * This method was called with True parameter676        /// 677        /// # Permissions678        /// * Collection Owner679        ///680        /// # Arguments681        /// 682        /// * collection_id.683        /// 684        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.685        #[weight = T::WeightInfo::set_mint_permission()]686        pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult687        {688            let sender = ensure_signed(origin)?;689690            Self::check_owner_permissions(collection_id, sender)?;691            let mut target_collection = <Collection<T>>::get(collection_id);692            target_collection.mint_mode = mint_permission;693            <Collection<T>>::insert(collection_id, target_collection);694695            Ok(())696        }697698        /// Change the owner of the collection.699        /// 700        /// # Permissions701        /// 702        /// * Collection Owner.703        /// 704        /// # Arguments705        /// 706        /// * collection_id.707        /// 708        /// * new_owner.709        #[weight = T::WeightInfo::change_collection_owner()]710        pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {711712            let sender = ensure_signed(origin)?;713            Self::check_owner_permissions(collection_id, sender)?;714            let mut target_collection = <Collection<T>>::get(collection_id);715            target_collection.owner = new_owner;716            <Collection<T>>::insert(collection_id, target_collection);717718            Ok(())719        }720721        /// Adds an admin of the Collection.722        /// 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. 723        /// 724        /// # Permissions725        /// 726        /// * Collection Owner.727        /// * Collection Admin.728        /// 729        /// # Arguments730        /// 731        /// * collection_id: ID of the Collection to add admin for.732        /// 733        /// * new_admin_id: Address of new admin to add.734        #[weight = T::WeightInfo::add_collection_admin()]735        pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {736737            let sender = ensure_signed(origin)?;738            Self::check_owner_or_admin_permissions(collection_id, sender)?;739            let mut admin_arr: Vec<T::AccountId> = Vec::new();740741            if <AdminList<T>>::contains_key(collection_id)742            {743                admin_arr = <AdminList<T>>::get(collection_id);744                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);745            }746747            // Number of collection admins748            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);749750            admin_arr.push(new_admin_id);751            <AdminList<T>>::insert(collection_id, admin_arr);752753            Ok(())754        }755756        /// 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.757        ///758        /// # Permissions759        /// 760        /// * Collection Owner.761        /// * Collection Admin.762        /// 763        /// # Arguments764        /// 765        /// * collection_id: ID of the Collection to remove admin for.766        /// 767        /// * account_id: Address of admin to remove.768        #[weight = T::WeightInfo::remove_collection_admin()]769        pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {770771            let sender = ensure_signed(origin)?;772            Self::check_owner_or_admin_permissions(collection_id, sender)?;773774            if <AdminList<T>>::contains_key(collection_id)775            {776                let mut admin_arr = <AdminList<T>>::get(collection_id);777                admin_arr.retain(|i| *i != account_id);778                <AdminList<T>>::insert(collection_id, admin_arr);779            }780781            Ok(())782        }783784        /// # Permissions785        /// 786        /// * Collection Owner787        /// 788        /// # Arguments789        /// 790        /// * collection_id.791        /// 792        /// * new_sponsor.793        #[weight = T::WeightInfo::set_collection_sponsor()]794        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {795796            let sender = ensure_signed(origin)?;797            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);798799            let mut target_collection = <Collection<T>>::get(collection_id);800            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);801802            target_collection.unconfirmed_sponsor = new_sponsor;803            <Collection<T>>::insert(collection_id, target_collection);804805            Ok(())806        }807808        /// # Permissions809        /// 810        /// * Sponsor.811        /// 812        /// # Arguments813        /// 814        /// * collection_id.815        #[weight = T::WeightInfo::confirm_sponsorship()]816        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {817818            let sender = ensure_signed(origin)?;819            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);820821            let mut target_collection = <Collection<T>>::get(collection_id);822            ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);823824            target_collection.sponsor = target_collection.unconfirmed_sponsor;825            target_collection.unconfirmed_sponsor = T::AccountId::default();826            <Collection<T>>::insert(collection_id, target_collection);827828            Ok(())829        }830831        /// Switch back to pay-per-own-transaction model.832        ///833        /// # Permissions834        ///835        /// * Collection owner.836        /// 837        /// # Arguments838        /// 839        /// * collection_id.840        #[weight = T::WeightInfo::remove_collection_sponsor()]841        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {842843            let sender = ensure_signed(origin)?;844            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);845846            let mut target_collection = <Collection<T>>::get(collection_id);847            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);848849            target_collection.sponsor = T::AccountId::default();850            <Collection<T>>::insert(collection_id, target_collection);851852            Ok(())853        }854855        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.856        /// 857        /// # Permissions858        /// 859        /// * Collection Owner.860        /// * Collection Admin.861        /// * Anyone if862        ///     * White List is enabled, and863        ///     * Address is added to white list, and864        ///     * MintPermission is enabled (see SetMintPermission method)865        /// 866        /// # Arguments867        /// 868        /// * collection_id: ID of the collection.869        /// 870        /// * owner: Address, initial owner of the NFT.871        ///872        /// * data: Token data to store on chain.873        // #[weight =874        // (130_000_000 as Weight)875        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))876        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))877        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]878879        #[weight = T::WeightInfo::create_item(data.len())]880        pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {881882            let sender = ensure_signed(origin)?;883884            Self::collection_exists(collection_id)?;885886            let target_collection = <Collection<T>>::get(collection_id);887888            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;889            Self::validate_create_item_args(&target_collection, &data)?;890            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;891892            Ok(())893        }894895        /// This method creates multiple instances of NFT Collection created with CreateCollection method.896        /// 897        /// # Permissions898        /// 899        /// * Collection Owner.900        /// * Collection Admin.901        /// * Anyone if902        ///     * White List is enabled, and903        ///     * Address is added to white list, and904        ///     * MintPermission is enabled (see SetMintPermission method)905        /// 906        /// # Arguments907        /// 908        /// * collection_id: ID of the collection.909        /// 910        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].911        /// 912        /// * owner: Address, initial owner of the NFT.913        #[weight = T::WeightInfo::create_item(items_data.into_iter()914                               .map(|data| { data.len() })915                               .sum())]916        pub fn create_multiple_items(origin, collection_id: u64, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {917918            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);919            let sender = ensure_signed(origin)?;920921            Self::collection_exists(collection_id)?;922            let target_collection = <Collection<T>>::get(collection_id);923924            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;925926            for data in &items_data {927                Self::validate_create_item_args(&target_collection, data)?;928            }929            for data in &items_data {930                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;931            }932933            Ok(())934        }935936        /// Destroys a concrete instance of NFT.937        /// 938        /// # Permissions939        /// 940        /// * Collection Owner.941        /// * Collection Admin.942        /// * Current NFT Owner.943        /// 944        /// # Arguments945        /// 946        /// * collection_id: ID of the collection.947        /// 948        /// * item_id: ID of NFT to burn.949        #[weight = T::WeightInfo::burn_item()]950        pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {951952            let sender = ensure_signed(origin)?;953            Self::collection_exists(collection_id)?;954955            // Transfer permissions check956            let target_collection = <Collection<T>>::get(collection_id);957            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||958                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),959                Error::<T>::NoPermission);960961            if target_collection.access == AccessMode::WhiteList {962                Self::check_white_list(collection_id, &sender)?;963            }964965            match target_collection.mode966            {967                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,968                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,969                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,970                _ => ()971            };972973            // call event974            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));975976            Ok(())977        }978979        /// Change ownership of the token.980        /// 981        /// # Permissions982        /// 983        /// * Collection Owner984        /// * Collection Admin985        /// * Current NFT owner986        ///987        /// # Arguments988        /// 989        /// * recipient: Address of token recipient.990        /// 991        /// * collection_id.992        /// 993        /// * item_id: ID of the item994        ///     * Non-Fungible Mode: Required.995        ///     * Fungible Mode: Ignored.996        ///     * Re-Fungible Mode: Required.997        /// 998        /// * value: Amount to transfer.999        ///     * Non-Fungible Mode: Ignored1000        ///     * Fungible Mode: Must specify transferred amount1001        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1002        #[weight = T::WeightInfo::transfer()]1003        pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {10041005            let sender = ensure_signed(origin)?;10061007            // Transfer permissions check1008            let target_collection = <Collection<T>>::get(collection_id);1009            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1010                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1011                Error::<T>::NoPermission);10121013            if target_collection.access == AccessMode::WhiteList {1014                Self::check_white_list(collection_id, &sender)?;1015                Self::check_white_list(collection_id, &recipient)?;1016            }10171018            match target_collection.mode1019            {1020                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1021                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1022                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1023                _ => ()1024            };10251026            Ok(())1027        }10281029        /// Set, change, or remove approved address to transfer the ownership of the NFT.1030        /// 1031        /// # Permissions1032        /// 1033        /// * Collection Owner1034        /// * Collection Admin1035        /// * Current NFT owner1036        /// 1037        /// # Arguments1038        /// 1039        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1040        /// 1041        /// * collection_id.1042        /// 1043        /// * item_id: ID of the item.1044        #[weight = T::WeightInfo::approve()]1045        pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {10461047            let sender = ensure_signed(origin)?;10481049            // Transfer permissions check1050            let target_collection = <Collection<T>>::get(collection_id);1051            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1052                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1053                Error::<T>::NoPermission);10541055            if target_collection.access == AccessMode::WhiteList {1056                Self::check_white_list(collection_id, &sender)?;1057                Self::check_white_list(collection_id, &approved)?;1058            }10591060            // amount param stub1061            let amount = 100000000;10621063            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1064            if list_exists {10651066                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1067                let item_contains = list.iter().any(|i| i.approved == approved);10681069                if !item_contains {1070                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1071                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1072                }1073            } else {10741075                let mut list = Vec::new();1076                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1077                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1078            }10791080            Ok(())1081        }1082        1083        /// 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.1084        /// 1085        /// # Permissions1086        /// * Collection Owner1087        /// * Collection Admin1088        /// * Current NFT owner1089        /// * Address approved by current NFT owner1090        /// 1091        /// # Arguments1092        /// 1093        /// * from: Address that owns token.1094        /// 1095        /// * recipient: Address of token recipient.1096        /// 1097        /// * collection_id.1098        /// 1099        /// * item_id: ID of the item.1100        /// 1101        /// * value: Amount to transfer.1102        #[weight = T::WeightInfo::transfer_from()]1103        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {11041105            let sender = ensure_signed(origin)?;1106            let mut appoved_transfer = false;11071108            // Check approve1109            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1110                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1111                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1112                if opt_item.is_some()1113                {1114                    appoved_transfer = true;1115                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1116                }1117            }11181119            // Transfer permissions check1120            let target_collection = <Collection<T>>::get(collection_id);1121                ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1122                Error::<T>::NoPermission);11231124            if target_collection.access == AccessMode::WhiteList {1125                Self::check_white_list(collection_id, &sender)?;1126                Self::check_white_list(collection_id, &recipient)?;1127            }11281129            // remove approve1130            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1131                .into_iter().filter(|i| i.approved != sender.clone()).collect();1132            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);113311341135            match target_collection.mode1136            {1137                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1138                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1139                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1140                _ => ()1141            };11421143            Ok(())1144        }11451146        ///1147        #[weight = 0]1148        pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11491150            // let no_perm_mes = "You do not have permissions to modify this collection";1151            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1152            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1153            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11541155            // // on_nft_received  call11561157            // Self::transfer(origin, collection_id, item_id, new_owner)?;11581159            Ok(())1160        }11611162        /// Set off-chain data schema.1163        /// 1164        /// # Permissions1165        /// 1166        /// * Collection Owner1167        /// * Collection Admin1168        /// 1169        /// # Arguments1170        /// 1171        /// * collection_id.1172        /// 1173        /// * schema: String representing the offchain data schema.1174        #[weight = T::WeightInfo::set_variable_meta_data()]1175        pub fn set_variable_meta_data (1176            origin,1177            collection_id: u64,1178            item_id: u64,1179            data: Vec<u8>1180        ) -> DispatchResult {1181            let sender = ensure_signed(origin)?;1182            1183            Self::collection_exists(collection_id)?;11841185            // Modify permissions check1186            let target_collection = <Collection<T>>::get(collection_id);1187            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1188                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1189                Error::<T>::NoPermission);11901191            Self::item_exists(collection_id, item_id, &target_collection.mode)?;11921193            match target_collection.mode1194            {1195                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1196                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1197                _ => ()1198            };11991200            Ok(())1201        }1202        12031204        /// Set off-chain data schema.1205        /// 1206        /// # Permissions1207        /// 1208        /// * Collection Owner1209        /// * Collection Admin1210        /// 1211        /// # Arguments1212        /// 1213        /// * collection_id.1214        /// 1215        /// * schema: String representing the offchain data schema.1216        #[weight = T::WeightInfo::set_offchain_schema()]1217        pub fn set_offchain_schema(1218            origin,1219            collection_id: u64,1220            schema: Vec<u8>1221        ) -> DispatchResult {1222            let sender = ensure_signed(origin)?;1223            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12241225            let mut target_collection = <Collection<T>>::get(collection_id);1226            target_collection.offchain_schema = schema;1227            <Collection<T>>::insert(collection_id, target_collection);12281229            Ok(())1230        }12311232        /// Set const on-chain data schema.1233        /// 1234        /// # Permissions1235        /// 1236        /// * Collection Owner1237        /// * Collection Admin1238        /// 1239        /// # Arguments1240        /// 1241        /// * collection_id.1242        /// 1243        /// * schema: String representing the const on-chain data schema.1244        #[weight = T::WeightInfo::set_const_on_chain_schema()]1245        pub fn set_const_on_chain_schema (1246            origin,1247            collection_id: u64,1248            schema: Vec<u8>1249        ) -> DispatchResult {1250            let sender = ensure_signed(origin)?;1251            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12521253            let mut target_collection = <Collection<T>>::get(collection_id);1254            target_collection.const_on_chain_schema = schema;1255            <Collection<T>>::insert(collection_id, target_collection);12561257            Ok(())1258        }12591260        /// Set variable on-chain data schema.1261        /// 1262        /// # Permissions1263        /// 1264        /// * Collection Owner1265        /// * Collection Admin1266        /// 1267        /// # Arguments1268        /// 1269        /// * collection_id.1270        /// 1271        /// * schema: String representing the variable on-chain data schema.1272        #[weight = T::WeightInfo::set_const_on_chain_schema()]1273        pub fn set_variable_on_chain_schema (1274            origin,1275            collection_id: u64,1276            schema: Vec<u8>1277        ) -> DispatchResult {1278            let sender = ensure_signed(origin)?;1279            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12801281            let mut target_collection = <Collection<T>>::get(collection_id);1282            target_collection.variable_on_chain_schema = schema;1283            <Collection<T>>::insert(collection_id, target_collection);12841285            Ok(())1286        }12871288        // Sudo permissions function1289        #[weight = 0]1290        pub fn set_chain_limits(1291            origin,1292            limits: ChainLimits1293        ) -> DispatchResult {1294            ensure_root(origin)?;1295            <ChainLimit>::put(limits);1296            Ok(())1297        }12981299        /// Enable smart contract self-sponsoring.1300        /// 1301        /// # Permissions1302        /// 1303        /// * Contract Owner1304        /// 1305        /// # Arguments1306        /// 1307        /// * contract address1308        /// * enable flag1309        /// 1310        #[weight = 0]1311        pub fn enable_contract_sponsoring(1312            origin,1313            contract_address: T::AccountId,1314            enable: bool1315        ) -> DispatchResult {1316            let sender = ensure_signed(origin)?;1317            let mut is_owner = false;1318            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1319                let owner = <ContractOwner<T>>::get(&contract_address);1320                is_owner = sender == owner;1321            }1322            ensure!(is_owner, Error::<T>::NoPermission);13231324            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1325            Ok(())1326        }13271328        /// Set the rate limit for contract sponsoring to specified number of blocks.1329        /// 1330        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1331        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1332        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1333        /// from contract endowment if there are at least B blocks between such transactions. 1334        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1335        /// 1336        /// # Permissions1337        /// 1338        /// * Contract Owner1339        /// 1340        /// # Arguments1341        /// 1342        /// -`contract_address`: Address of the contract to sponsor1343        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1344        /// 1345        #[weight = 0]1346        pub fn set_contract_sponsoring_rate_limit(1347            origin,1348            contract_address: T::AccountId,1349            rate_limit: T::BlockNumber1350        ) -> DispatchResult {1351            let sender = ensure_signed(origin)?;1352            let mut is_owner = false;1353            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1354                let owner = <ContractOwner<T>>::get(&contract_address);1355                is_owner = sender == owner;1356            }1357            ensure!(is_owner, Error::<T>::NoPermission);13581359            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1360            Ok(())1361        }13621363    }1364}13651366impl<T: Trait> Module<T> {13671368    fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {13691370        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1371            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1372            Self::check_white_list(collection_id, owner)?;1373            Self::check_white_list(collection_id, sender)?;1374        }13751376        Ok(())1377    }13781379    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1380        match target_collection.mode1381        {1382            CollectionMode::NFT => {1383                if let CreateItemData::NFT(data) = data {1384                    // check sizes1385                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");1386                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");1387                } else {1388                    fail!("Not NFT item data used to mint in NFT collection.");1389                }1390            },1391            CollectionMode::Fungible(_) => {1392                if let CreateItemData::Fungible(_) = data {1393                } else {1394                    fail!("Not Fungible item data used to mint in Fungible collection.");1395                }1396            },1397            CollectionMode::ReFungible(_) => {1398                if let CreateItemData::ReFungible(data) = data {13991400                    // check sizes1401                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");1402                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");1403                } else {1404                    fail!("Not Re Fungible item data used to mint in Re Fungible collection.");1405                }1406            },1407            _ => { fail!("Unexpected collection type."); }1408        };14091410        Ok(())1411    }14121413    fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1414        match data1415        {1416            CreateItemData::NFT(data) => {1417                let item = NftItemType {1418                    collection: collection_id,1419                    owner,1420                    const_data: data.const_data,1421                    variable_data: data.variable_data1422                };14231424                Self::add_nft_item(item)?;1425            },1426            CreateItemData::Fungible(_) => {1427                let item = FungibleItemType {1428                    collection: collection_id,1429                    owner,1430                    value: (10 as u128).pow(collection.decimal_points)1431                };14321433                Self::add_fungible_item(item)?;1434            },1435            CreateItemData::ReFungible(data) => {1436                let mut owner_list = Vec::new();1437                let value = (10 as u128).pow(collection.decimal_points);1438                owner_list.push(Ownership {owner: owner.clone(), fraction: value});14391440                let item = ReFungibleItemType {1441                    collection: collection_id,1442                    owner: owner_list,1443                    const_data: data.const_data,1444                    variable_data: data.variable_data1445                };14461447                Self::add_refungible_item(item)?;1448            }1449        };145014511452        // call event1453        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));14541455        Ok(())1456    }14571458    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1459        let current_index = <ItemListIndex>::get(item.collection)1460            .checked_add(1)1461            .ok_or(Error::<T>::NumOverflow)?;1462        let itemcopy = item.clone();1463        let owner = item.owner.clone();1464        let value = item.value as u64;14651466        Self::add_token_index(item.collection, current_index, owner.clone())?;14671468        <ItemListIndex>::insert(item.collection, current_index);1469        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14701471        // Add current block1472        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1473        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1474        1475        // Update balance1476        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1477            .checked_add(value)1478            .ok_or(Error::<T>::NumOverflow)?;1479        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14801481        Ok(())1482    }14831484    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1485        let current_index = <ItemListIndex>::get(item.collection)1486            .checked_add(1)1487            .ok_or(Error::<T>::NumOverflow)?;1488        let itemcopy = item.clone();14891490        let value = item.owner.first().unwrap().fraction as u64;1491        let owner = item.owner.first().unwrap().owner.clone();14921493        Self::add_token_index(item.collection, current_index, owner.clone())?;14941495        <ItemListIndex>::insert(item.collection, current_index);1496        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14971498        // Add current block1499        let block_number: T::BlockNumber = 0.into();1500        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);15011502        // Update balance1503        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1504            .checked_add(value)1505            .ok_or(Error::<T>::NumOverflow)?;1506        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15071508        Ok(())1509    }15101511    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1512        let current_index = <ItemListIndex>::get(item.collection)1513            .checked_add(1)1514            .ok_or(Error::<T>::NumOverflow)?;15151516        let item_owner = item.owner.clone();1517        let collection_id = item.collection.clone();1518        Self::add_token_index(collection_id, current_index, item.owner.clone())?;15191520        <ItemListIndex>::insert(collection_id, current_index);1521        <NftItemList<T>>::insert(collection_id, current_index, item);15221523        // Add current block1524        let block_number: T::BlockNumber = 0.into();1525        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);15261527        // Update balance1528        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1529            .checked_add(1)1530            .ok_or(Error::<T>::NumOverflow)?;1531        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);15321533        Ok(())1534    }15351536    fn burn_refungible_item(1537        collection_id: u64,1538        item_id: u64,1539        owner: T::AccountId,1540    ) -> DispatchResult {1541        ensure!(1542            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1543            Error::<T>::TokenNotFound1544        );1545        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1546        let item = collection1547            .owner1548            .iter()1549            .filter(|&i| i.owner == owner)1550            .next()1551            .unwrap();1552        Self::remove_token_index(collection_id, item_id, owner.clone())?;15531554        // remove approve list1555        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));15561557        // update balance1558        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1559            .checked_sub(item.fraction as u64)1560            .ok_or(Error::<T>::NumOverflow)?;1561        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15621563        <ReFungibleItemList<T>>::remove(collection_id, item_id);15641565        Ok(())1566    }15671568    fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1569        ensure!(1570            <NftItemList<T>>::contains_key(collection_id, item_id),1571            Error::<T>::TokenNotFound1572        );1573        let item = <NftItemList<T>>::get(collection_id, item_id);1574        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15751576        // remove approve list1577        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15781579        // update balance1580        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1581            .checked_sub(1)1582            .ok_or(Error::<T>::NumOverflow)?;1583        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1584        <NftItemList<T>>::remove(collection_id, item_id);15851586        Ok(())1587    }15881589    fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1590        ensure!(1591            <FungibleItemList<T>>::contains_key(collection_id, item_id),1592            Error::<T>::TokenNotFound1593        );1594        let item = <FungibleItemList<T>>::get(collection_id, item_id);1595        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15961597        // remove approve list1598        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15991600        // update balance1601        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1602            .checked_sub(item.value as u64)1603            .ok_or(Error::<T>::NumOverflow)?;1604        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);16051606        <FungibleItemList<T>>::remove(collection_id, item_id);16071608        Ok(())1609    }16101611    fn collection_exists(collection_id: u64) -> DispatchResult {1612        ensure!(1613            <Collection<T>>::contains_key(collection_id),1614            Error::<T>::CollectionNotFound1615        );1616        Ok(())1617    }16181619    fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1620        Self::collection_exists(collection_id)?;16211622        let target_collection = <Collection<T>>::get(collection_id);1623        ensure!(1624            subject == target_collection.owner,1625            Error::<T>::NoPermission1626        );16271628        Ok(())1629    }16301631    fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1632        let target_collection = <Collection<T>>::get(collection_id);1633        let mut result: bool = subject == target_collection.owner;1634        let exists = <AdminList<T>>::contains_key(collection_id);16351636        if !result & exists {1637            if <AdminList<T>>::get(collection_id).contains(&subject) {1638                result = true1639            }1640        }16411642        result1643    }16441645    fn check_owner_or_admin_permissions(1646        collection_id: u64,1647        subject: T::AccountId,1648    ) -> DispatchResult {1649        Self::collection_exists(collection_id)?;1650        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());16511652        ensure!(1653            result,1654            Error::<T>::NoPermission1655        );1656        Ok(())1657    }16581659    fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1660        let target_collection = <Collection<T>>::get(collection_id);16611662        match target_collection.mode {1663            CollectionMode::NFT => {1664                <NftItemList<T>>::get(collection_id, item_id).owner == subject1665            }1666            CollectionMode::Fungible(_) => {1667                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1668            }1669            CollectionMode::ReFungible(_) => {1670                <ReFungibleItemList<T>>::get(collection_id, item_id)1671                    .owner1672                    .iter()1673                    .any(|i| i.owner == subject)1674            }1675            CollectionMode::Invalid => false,1676        }1677    }16781679    fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1680        let mes = Error::<T>::AddresNotInWhiteList;1681        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1682        let wl = <WhiteList<T>>::get(collection_id);1683        ensure!(wl.contains(address), mes);16841685        Ok(())1686    }16871688    fn transfer_fungible(1689        collection_id: u64,1690        item_id: u64,1691        value: u64,1692        owner: T::AccountId,1693        new_owner: T::AccountId,1694    ) -> DispatchResult {1695        ensure!(1696            <FungibleItemList<T>>::contains_key(collection_id, item_id),1697            Error::<T>::TokenNotFound1698        );16991700        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1701        let amount = full_item.value;17021703        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);17041705        // update balance1706        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1707            .checked_sub(value)1708            .ok_or(Error::<T>::NumOverflow)?;1709        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);17101711        let mut new_owner_account_id = 0;1712        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1713        if new_owner_items.len() > 0 {1714            new_owner_account_id = new_owner_items[0];1715        }17161717        let val64 = value.into();17181719        // transfer1720        if amount == val64 && new_owner_account_id == 0 {1721            // change owner1722            // new owner do not have account1723            let mut new_full_item = full_item.clone();1724            new_full_item.owner = new_owner.clone();1725            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17261727            // update balance1728            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1729                .checked_add(value)1730                .ok_or(Error::<T>::NumOverflow)?;1731            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17321733            // update index collection1734            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1735        } else {1736            let mut new_full_item = full_item.clone();1737            new_full_item.value -= val64;17381739            // separate amount1740            if new_owner_account_id > 0 {1741                // new owner has account1742                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1743                item.value += val64;17441745                // update balance1746                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1747                    .checked_add(value)1748                    .ok_or(Error::<T>::NumOverflow)?;1749                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17501751                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1752            } else {1753                // new owner do not have account1754                let item = FungibleItemType {1755                    collection: collection_id,1756                    owner: new_owner.clone(),1757                    value: val64,1758                };17591760                Self::add_fungible_item(item)?;1761            }17621763            if amount == val64 {1764                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;17651766                // remove approve list1767                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1768                <FungibleItemList<T>>::remove(collection_id, item_id);1769            }17701771            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1772        }17731774        Ok(())1775    }17761777    fn transfer_refungible(1778        collection_id: u64,1779        item_id: u64,1780        value: u64,1781        owner: T::AccountId,1782        new_owner: T::AccountId,1783    ) -> DispatchResult {1784        ensure!(1785            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1786            Error::<T>::TokenNotFound1787        );17881789        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1790        let item = full_item1791            .owner1792            .iter()1793            .filter(|i| i.owner == owner)1794            .next()1795            .ok_or(Error::<T>::NumOverflow)?;1796        let amount = item.fraction;17971798        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);17991800        // update balance1801        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1802            .checked_sub(value)1803            .ok_or(Error::<T>::NumOverflow)?;1804        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18051806        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1807            .checked_add(value)1808            .ok_or(Error::<T>::NumOverflow)?;1809        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18101811        let old_owner = item.owner.clone();1812        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1813        let val64 = value.into();18141815        // transfer1816        if amount == val64 && !new_owner_has_account {1817            // change owner1818            // new owner do not have account1819            let mut new_full_item = full_item.clone();1820            new_full_item1821                .owner1822                .iter_mut()1823                .find(|i| i.owner == owner)1824                .unwrap()1825                .owner = new_owner.clone();1826            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18271828            // update index collection1829            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1830        } else {1831            let mut new_full_item = full_item.clone();1832            new_full_item1833                .owner1834                .iter_mut()1835                .find(|i| i.owner == owner)1836                .unwrap()1837                .fraction -= val64;18381839            // separate amount1840            if new_owner_has_account {1841                // new owner has account1842                new_full_item1843                    .owner1844                    .iter_mut()1845                    .find(|i| i.owner == new_owner)1846                    .unwrap()1847                    .fraction += val64;1848            } else {1849                // new owner do not have account1850                new_full_item.owner.push(Ownership {1851                    owner: new_owner.clone(),1852                    fraction: val64,1853                });1854                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1855            }18561857            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1858        }18591860        Ok(())1861    }18621863    fn transfer_nft(1864        collection_id: u64,1865        item_id: u64,1866        sender: T::AccountId,1867        new_owner: T::AccountId,1868    ) -> DispatchResult {1869        ensure!(1870            <NftItemList<T>>::contains_key(collection_id, item_id),1871            Error::<T>::TokenNotFound1872        );18731874        let mut item = <NftItemList<T>>::get(collection_id, item_id);18751876        ensure!(1877            sender == item.owner,1878            Error::<T>::MustBeTokenOwner1879        );18801881        // update balance1882        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1883            .checked_sub(1)1884            .ok_or(Error::<T>::NumOverflow)?;1885        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18861887        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1888            .checked_add(1)1889            .ok_or(Error::<T>::NumOverflow)?;1890        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18911892        // change owner1893        let old_owner = item.owner.clone();1894        item.owner = new_owner.clone();1895        <NftItemList<T>>::insert(collection_id, item_id, item);18961897        // update index collection1898        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;18991900        // reset approved list1901        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1902        Ok(())1903    }1904    1905    fn item_exists(1906        collection_id: u64,1907        item_id: u64,1908        mode: &CollectionMode1909    ) -> DispatchResult {1910        match mode {1911            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1912            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1913            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1914            _ => ()1915        };1916        1917        Ok(())1918    }19191920    fn set_re_fungible_variable_data(1921        collection_id: u64,1922        item_id: u64,1923        data: Vec<u8>1924    ) -> DispatchResult {1925        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);19261927        item.variable_data = data;19281929        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);19301931        Ok(())1932    }19331934    fn set_nft_variable_data(1935        collection_id: u64,1936        item_id: u64,1937        data: Vec<u8>1938    ) -> DispatchResult {1939        let mut item = <NftItemList<T>>::get(collection_id, item_id);1940        1941        item.variable_data = data;19421943        <NftItemList<T>>::insert(collection_id, item_id, item);1944        1945        Ok(())1946    }19471948    fn init_collection(item: &CollectionType<T::AccountId>) {1949        // check params1950        assert!(1951            item.decimal_points <= 4,1952            "decimal_points parameter must be lower than 4"1953        );1954        assert!(1955            item.name.len() <= 64,1956            "Collection name can not be longer than 63 char"1957        );1958        assert!(1959            item.name.len() <= 256,1960            "Collection description can not be longer than 255 char"1961        );1962        assert!(1963            item.token_prefix.len() <= 16,1964            "Token prefix can not be longer than 15 char"1965        );19661967        // Generate next collection ID1968        let next_id = CreatedCollectionCount::get()1969            .checked_add(1)1970            .unwrap();19711972        CreatedCollectionCount::put(next_id);1973    }19741975    fn init_nft_token(item: &NftItemType<T::AccountId>) {1976        let current_index = <ItemListIndex>::get(item.collection)1977            .checked_add(1)1978            .unwrap();19791980        let item_owner = item.owner.clone();1981        let collection_id = item.collection.clone();1982        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();19831984        <ItemListIndex>::insert(collection_id, current_index);19851986        // Update balance1987        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1988            .checked_add(1)1989            .unwrap();1990        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1991    }19921993    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1994        let current_index = <ItemListIndex>::get(item.collection)1995            .checked_add(1)1996            .unwrap();1997        let owner = item.owner.clone();1998        let value = item.value as u64;19992000        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20012002        <ItemListIndex>::insert(item.collection, current_index);20032004        // Update balance2005        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2006            .checked_add(value)2007            .unwrap();2008        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2009    }20102011    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2012        let current_index = <ItemListIndex>::get(item.collection)2013            .checked_add(1)2014            .unwrap();20152016        let value = item.owner.first().unwrap().fraction as u64;2017        let owner = item.owner.first().unwrap().owner.clone();20182019        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20202021        <ItemListIndex>::insert(item.collection, current_index);20222023        // Update balance2024        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2025            .checked_add(value)2026            .unwrap();2027        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2028    }20292030    fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {20312032        // add to account limit2033        if <AccountItemCount<T>>::contains_key(owner.clone()) {20342035            // bound Owned tokens by a single address2036            let count = <AccountItemCount<T>>::get(owner.clone());2037            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);20382039            <AccountItemCount<T>>::insert(owner.clone(), count2040                .checked_add(1)2041                .ok_or(Error::<T>::NumOverflow)?);2042        }2043        else {2044            <AccountItemCount<T>>::insert(owner.clone(), 1);2045        }20462047        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2048        if list_exists {2049            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2050            let item_contains = list.contains(&item_index.clone());20512052            if !item_contains {2053                list.push(item_index.clone());2054            }20552056            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2057        } else {2058            let mut itm = Vec::new();2059            itm.push(item_index.clone());2060            <AddressTokens<T>>::insert(collection_id, owner, itm);2061            2062        }20632064        Ok(())2065    }20662067    fn remove_token_index(2068        collection_id: u64,2069        item_index: u64,2070        owner: T::AccountId,2071    ) -> DispatchResult {20722073        // update counter2074        <AccountItemCount<T>>::insert(owner.clone(), 2075            <AccountItemCount<T>>::get(owner.clone())2076            .checked_sub(1)2077            .ok_or(Error::<T>::NumOverflow)?);207820792080        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2081        if list_exists {2082            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2083            let item_contains = list.contains(&item_index.clone());20842085            if item_contains {2086                list.retain(|&item| item != item_index);2087                <AddressTokens<T>>::insert(collection_id, owner, list);2088            }2089        }20902091        Ok(())2092    }20932094    fn move_token_index(2095        collection_id: u64,2096        item_index: u64,2097        old_owner: T::AccountId,2098        new_owner: T::AccountId,2099    ) -> DispatchResult {2100        Self::remove_token_index(collection_id, item_index, old_owner)?;2101        Self::add_token_index(collection_id, item_index, new_owner)?;21022103        Ok(())2104    }2105}21062107////////////////////////////////////////////////////////////////////////////////////////////////////2108// Economic models2109// #region21102111/// Fee multiplier.2112pub type Multiplier = FixedU128;21132114type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2115    <T as system::Trait>::AccountId,2116>>::Balance;2117type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2118    <T as system::Trait>::AccountId,2119>>::NegativeImbalance;21202121/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2122/// in the queue.2123#[derive(Encode, Decode, Clone, Eq, PartialEq)]2124pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2125    #[codec(compact)] BalanceOf<T>2126);21272128impl<T: Trait + Send + Sync> sp_std::fmt::Debug2129    for ChargeTransactionPayment<T>2130{2131    #[cfg(feature = "std")]2132    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2133        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2134    }2135    #[cfg(not(feature = "std"))]2136    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2137        Ok(())2138    }2139}21402141impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2142where2143    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2144    BalanceOf<T>: Send + Sync + FixedPointOperand,2145{2146    /// utility constructor. Used only in client/factory code.2147    pub fn from(fee: BalanceOf<T>) -> Self {2148        Self(fee)2149    }21502151    pub fn traditional_fee(2152        len: usize,2153        info: &DispatchInfoOf<T::Call>,2154        tip: BalanceOf<T>,2155    ) -> BalanceOf<T>2156    where2157        T::Call: Dispatchable<Info = DispatchInfo>,2158    {2159        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2160    }21612162    fn withdraw_fee(2163        &self,2164        who: &T::AccountId,2165        call: &T::Call,2166        info: &DispatchInfoOf<T::Call>,2167        len: usize,2168    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2169        let tip = self.0;21702171        // Set fee based on call type. Creating collection costs 1 Unique.2172        // All other transactions have traditional fees so far2173        // let fee = match call.is_sub_type() {2174        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2175        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2176        //                                                 // _ => <BalanceOf<T>>::from(100)2177        // };2178        let fee = Self::traditional_fee(len, info, tip);21792180        // Determine who is paying transaction fee based on ecnomic model2181        // Parse call to extract collection ID and access collection sponsor2182        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2183            Some(Call::create_item(collection_id, _properties, _owner)) => {2184                <Collection<T>>::get(collection_id).sponsor2185            }2186            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2187                let _collection_mode = <Collection<T>>::get(collection_id).mode;21882189                // sponsor timeout2190                let sponsor_transfer = match _collection_mode {2191                    CollectionMode::NFT => {2192                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2193                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2194                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2195                        if block_number >= limit_time {2196                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2197                            true2198                        }2199                        else {2200                            false2201                        }2202                    }2203                    CollectionMode::Fungible(_) => {2204                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2205                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2206                        if basket.iter().any(|i| i.address == _new_owner.clone())2207                        {2208                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2209                            let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2210                            if block_number >= limit_time {2211                                basket.retain(|x| x.address == item.address);2212                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2213                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2214                                true2215                            }2216                            else {2217                                false2218                            }2219                        }2220                        else {2221                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2222                            true2223                        }2224                    }2225                    CollectionMode::ReFungible(_) => {2226                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2227                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2228                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2229                        if block_number >= limit_time {2230                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2231                            true2232                        } else {2233                            false2234                        }2235                    }2236                    _ => {2237                        false2238                    },2239                };22402241                if !sponsor_transfer {2242                    T::AccountId::default()2243                } else {2244                    <Collection<T>>::get(collection_id).sponsor2245                }2246            }22472248            _ => T::AccountId::default(),2249        };22502251        // Sponsor smart contracts2252        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {22532254            // On instantiation: set the contract owner2255            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {22562257                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2258                    code_hash,2259                    &data,2260                    &who,2261                );2262                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());22632264                T::AccountId::default()2265            },22662267            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2268            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {22692270                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());22712272                let mut sponsor_transfer = false;2273                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2274                    let last_tx_block = <ContractSponsorBasket<T>>::get(&called_contract);2275                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2276                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2277                    let limit_time = last_tx_block + rate_limit;22782279                    if block_number >= limit_time {2280                        <ContractSponsorBasket<T>>::insert(called_contract.clone(), block_number);2281                        sponsor_transfer = true;2282                    }2283                } else {2284                    sponsor_transfer = false;2285                }2286               2287                2288                let mut sp = T::AccountId::default();2289                if sponsor_transfer {2290                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2291                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2292                            sp = called_contract;2293                        }2294                    }2295                }22962297                sp2298            },22992300            _ => sponsor,2301        };23022303        let mut who_pays_fee: T::AccountId = sponsor.clone();2304        if sponsor == T::AccountId::default() {2305            who_pays_fee = who.clone();2306        }23072308        // Only mess with balances if fee is not zero.2309        if fee.is_zero() {2310            return Ok((fee, None));2311        }23122313        match <T as transaction_payment::Trait>::Currency::withdraw(2314            &who_pays_fee,2315            fee,2316            if tip.is_zero() {2317                WithdrawReason::TransactionPayment.into()2318            } else {2319                WithdrawReason::TransactionPayment | WithdrawReason::Tip2320            },2321            ExistenceRequirement::KeepAlive,2322        ) {2323            Ok(imbalance) => Ok((fee, Some(imbalance))),2324            Err(_) => Err(InvalidTransaction::Payment.into()),2325        }2326    }2327}232823292330impl<T: Trait + Send + Sync> SignedExtension2331    for ChargeTransactionPayment<T>2332where2333    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2334    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2335{2336    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2337    type AccountId = T::AccountId;2338    type Call = T::Call;2339    type AdditionalSigned = ();2340    type Pre = (2341        BalanceOf<T>,2342        Self::AccountId,2343        Option<NegativeImbalanceOf<T>>,2344        BalanceOf<T>,2345    );2346    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2347        Ok(())2348    }23492350    fn validate(2351        &self,2352        _who: &Self::AccountId,2353        _call: &Self::Call,2354        _info: &DispatchInfoOf<Self::Call>,2355        _len: usize,2356    ) -> TransactionValidity {2357        Ok(ValidTransaction::default())2358    }23592360    fn pre_dispatch(2361        self,2362        who: &Self::AccountId,2363        call: &Self::Call,2364        info: &DispatchInfoOf<Self::Call>,2365        len: usize,2366    ) -> Result<Self::Pre, TransactionValidityError> {2367        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2368        Ok((self.0, who.clone(), imbalance, fee))2369    }23702371    fn post_dispatch(2372        pre: Self::Pre,2373        info: &DispatchInfoOf<Self::Call>,2374        post_info: &PostDispatchInfoOf<Self::Call>,2375        len: usize,2376        _result: &DispatchResult,2377    ) -> Result<(), TransactionValidityError> {2378        let (tip, who, imbalance, fee) = pre;2379        if let Some(payed) = imbalance {2380            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2381                len as u32, info, post_info, tip,2382            );2383            let refund = fee.saturating_sub(actual_fee);2384            let actual_payment =2385                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2386                    &who, refund,2387                ) {2388                    Ok(refund_imbalance) => {2389                        // The refund cannot be larger than the up front payed max weight.2390                        // `PostDispatchInfo::calc_unspent` guards against such a case.2391                        match payed.offset(refund_imbalance) {2392                            Ok(actual_payment) => actual_payment,2393                            Err(_) => return Err(InvalidTransaction::Payment.into()),2394                        }2395                    }2396                    // We do not recreate the account using the refund. The up front payment2397                    // is gone in that case.2398                    Err(_) => payed,2399                };2400            let imbalances = actual_payment.split(tip);2401            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2402                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2403            );2404        }2405        Ok(())2406    }2407}24082409// #endregion
modifiedruntime/src/nft_weights.rsdiffbeforeafterboth
--- a/runtime/src/nft_weights.rs
+++ b/runtime/src/nft_weights.rs
@@ -108,4 +108,19 @@
             .saturating_add(DbWeight::get().reads(2 as Weight))
             .saturating_add(DbWeight::get().writes(1 as Weight))
     }
+    // fn set_chain_limits() -> Weight {
+    //     (0 as Weight)
+    //         .saturating_add(DbWeight::get().reads(1 as Weight))
+    //         .saturating_add(DbWeight::get().writes(1 as Weight))
+    // }
+    // fn enable_contract_sponsoring() -> Weight {
+    //     (0 as Weight)
+    //         .saturating_add(DbWeight::get().reads(1 as Weight))
+    //         .saturating_add(DbWeight::get().writes(1 as Weight))
+    // }
+    // fn set_contract_sponsoring_rate_limit() -> Weight {
+    //     (0 as Weight)
+    //         .saturating_add(DbWeight::get().reads(1 as Weight))
+    //         .saturating_add(DbWeight::get().writes(1 as Weight))
+    // }
 }