git.delta.rocks / unique-network / refs/commits / 7bcde70a2ca7

difftreelog

NFTPAR-118. Per Collection Limits

str-mv2020-12-11parent: #68e6834.patch.diff
in: master

3 files changed

modifiedREADME.mddiffbeforeafterboth
--- a/README.md
+++ b/README.md
@@ -241,6 +241,12 @@
       "ReFungible": "CreateReFungibleData"
     }
   }
+  "CollectionLimits": {
+    "AccountTokenOwnershipLimit": "u32",
+    "SponsoredDataSize": "u32",
+    "TokenLimit": "u32",
+    "SponsorTransferTimeout": "u32"
+  }
 }
 
 ```
\ No newline at end of file
modifiednode/src/chain_spec.rsdiffbeforeafterboth
--- a/node/src/chain_spec.rs
+++ b/node/src/chain_spec.rs
@@ -171,7 +171,8 @@
                     sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),
                     unconfirmed_sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),
                     const_on_chain_schema: vec![],
-                    variable_on_chain_schema: vec![]
+					variable_on_chain_schema: vec![],
+					limits: CollectionLimits::default()
                 },
             )],
             nft_item_id: vec![],
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
before · pallets/nft/src/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]23#[cfg(feature = "std")]4pub use std::*;56#[cfg(feature = "std")]7pub use serde::*;89use codec::{Decode, Encode};10pub use frame_support::{11    construct_runtime, decl_event, decl_module, decl_storage, decl_error,12    dispatch::DispatchResult,13    ensure, fail, parameter_types,14    traits::{15        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,16        Randomness, WithdrawReason,17    },18    weights::{19        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},20        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,21        WeightToFeePolynomial,22    },23    IsSubType, StorageValue,24};2526use frame_system::{self as system, ensure_signed, ensure_root};27use sp_runtime::sp_std::prelude::Vec;28use sp_runtime::{29    traits::{30        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SignedExtension, Zero,31    },32    transaction_validity::{33        InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,34    },35    FixedPointOperand, FixedU128,36};37use pallet_contracts::ContractAddressFor;38use sp_runtime::traits::StaticLookup;3940#[cfg(test)]41mod mock;4243#[cfg(test)]44mod tests;4546mod default_weights;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 exceeded.268        TotalCollectionsLimitExceeded,269		/// Decimal_points parameter must be lower than 4.270        CollectionDecimalPointLimitExceeded, 271        /// Collection name can not be longer than 63 char.272        CollectionNameLimitExceeded, 273        /// Collection description can not be longer than 255 char.274        CollectionDescriptionLimitExceeded, 275        /// Token prefix can not be longer than 15 char.276        CollectionTokenPrefixLimitExceeded,277        /// This collection does not exist.278        CollectionNotFound,279        /// Item not exists.280        TokenNotFound,281        /// Arithmetic calculation overflow.282        NumOverflow,       283        /// Account already has admin role.284        AlreadyAdmin,  285        /// You do not own this collection.286        NoPermission,287        /// This address is not set as sponsor, use setCollectionSponsor first.288        ConfirmUnsetSponsorFail,289        /// Collection is not in mint mode.290        PublicMintingNotAllowed,291        /// Sender parameter and item owner must be equal.292        MustBeTokenOwner,293        /// Item balance not enough.294        TokenValueTooLow,295        /// Size of item is too large.296        NftSizeLimitExceeded,297        /// No approve found298        ApproveNotFound,299        /// Requested value more than approved.300        TokenValueNotEnough,301        /// Only approved addresses can call this method.302        ApproveRequired,303        /// Address is not in white list.304        AddresNotInWhiteList,305        /// Number of collection admins bound exceeded.306        CollectionAdminsLimitExceeded,307        /// Owned tokens by a single address bound exceeded.308        AddressOwnershipLimitExceeded,309        /// Length of items properties must be greater than 0.310        EmptyArgument,311        /// const_data exceeded data limit.312        TokenConstDataLimitExceeded,313        /// variable_data exceeded data limit.314        TokenVariableDataLimitExceeded,315        /// Not NFT item data used to mint in NFT collection.316        NotNftDataUsedToMintNftCollectionToken,317        /// Not Fungible item data used to mint in Fungible collection.318        NotFungibleDataUsedToMintFungibleCollectionToken,319        /// Not Re Fungible item data used to mint in Re Fungible collection.320        NotReFungibleDataUsedToMintReFungibleCollectionToken,321        /// Unexpected collection type.322        UnexpectedCollectionType,323        /// Can't store metadata in fungible tokens.324        CantStoreMetadataInFungibleTokens325	}326}327328pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {329    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;330331    /// Weight information for extrinsics in this pallet.332	type WeightInfo: WeightInfo;333}334335#[cfg(feature = "runtime-benchmarks")]336mod benchmarking;337338// #endregion339340decl_storage! {341    trait Store for Module<T: Trait> as Nft {342343        // Private members344        NextCollectionID: u64;345        CreatedCollectionCount: u64;346        ChainVersion: u64;347        ItemListIndex: map hasher(blake2_128_concat) u64 => u64;348349        // Chain limits struct350        pub ChainLimit get(fn chain_limit) config(): ChainLimits;351352        // Bound counters353        CollectionCount: u64;354        pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;355356        // Basic collections357        pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;358        pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;359        pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;360361        /// Balance owner per collection map362        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;363364        /// second parameter: item id + owner account id365        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;366367        /// Item collections368        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;369        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;370        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;371372        /// Index list373        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;374375        /// Tokens transfer baskets376        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;377        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>>;378        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;379380        // Contract Sponsorship and Ownership381        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;382        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;383        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;384        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;385    }386    add_extra_genesis {387        build(|config: &GenesisConfig<T>| {388            // Modification of storage389            for (_num, _c) in &config.collection {390                <Module<T>>::init_collection(_c);391            }392393            for (_num, _q, _i) in &config.nft_item_id {394                <Module<T>>::init_nft_token(_i);395            }396397            for (_num, _q, _i) in &config.fungible_item_id {398                <Module<T>>::init_fungible_token(_i);399            }400401            for (_num, _q, _i) in &config.refungible_item_id {402                <Module<T>>::init_refungible_token(_i);403            }404        })405    }406}407408decl_event!(409    pub enum Event<T>410    where411        AccountId = <T as system::Trait>::AccountId,412    {413        /// New collection was created414        /// 415        /// # Arguments416        /// 417        /// * collection_id: Globally unique identifier of newly created collection.418        /// 419        /// * mode: [CollectionMode] converted into u8.420        /// 421        /// * account_id: Collection owner.422        Created(u64, u8, AccountId),423424        /// New item was created.425        /// 426        /// # Arguments427        /// 428        /// * collection_id: Id of the collection where item was created.429        /// 430        /// * item_id: Id of an item. Unique within the collection.431        ItemCreated(u64, u64),432433        /// Collection item was burned.434        /// 435        /// # Arguments436        /// 437        /// collection_id.438        /// 439        /// item_id: Identifier of burned NFT.440        ItemDestroyed(u64, u64),441    }442);443444decl_module! {445    pub struct Module<T: Trait> for enum Call where origin: T::Origin {446447        fn deposit_event() = default;448        type Error = Error<T>;449450        fn on_initialize(now: T::BlockNumber) -> Weight {451452            if ChainVersion::get() < 2453            {454                let value = NextCollectionID::get();455                CreatedCollectionCount::put(value);456                ChainVersion::put(2);457            }458459            0460        }461462        /// 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.463        /// 464        /// # Permissions465        /// 466        /// * Anyone.467        /// 468        /// # Arguments469        /// 470        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.471        /// 472        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.473        /// 474        /// * token_prefix: UTF-8 string with token prefix.475        /// 476        /// * mode: [CollectionMode] collection type and type dependent data.477        // returns collection ID478        #[weight = T::WeightInfo::create_collection()]479        pub fn create_collection(origin,480                                 collection_name: Vec<u16>,481                                 collection_description: Vec<u16>,482                                 token_prefix: Vec<u8>,483                                 mode: CollectionMode) -> DispatchResult {484485            // Anyone can create a collection486            let who = ensure_signed(origin)?;487488            let decimal_points = match mode {489                CollectionMode::Fungible(points) => points,490                CollectionMode::ReFungible(points) => points,491                _ => 0492            };493494            // bound Total number of collections495            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);496497            // check params498            ensure!(decimal_points <= 4, Error::<T>::CollectionDecimalPointLimitExceeded);499500            let mut name = collection_name.to_vec();501            name.push(0);502            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);503504            let mut description = collection_description.to_vec();505            description.push(0);506            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);507508            let mut prefix = token_prefix.to_vec();509            prefix.push(0);510            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);511512            // Generate next collection ID513            let next_id = CreatedCollectionCount::get()514                .checked_add(1)515                .ok_or(Error::<T>::NumOverflow)?;516517            // bound counter518            let total = CollectionCount::get()519                .checked_add(1)520                .ok_or(Error::<T>::NumOverflow)?;521522            CreatedCollectionCount::put(next_id);523            CollectionCount::put(total);524525            // Create new collection526            let new_collection = CollectionType {527                owner: who.clone(),528                name: name,529                mode: mode.clone(),530                mint_mode: false,531                access: AccessMode::Normal,532                description: description,533                decimal_points: decimal_points,534                token_prefix: prefix,535                offchain_schema: Vec::new(),536                sponsor: T::AccountId::default(),537                unconfirmed_sponsor: T::AccountId::default(),538                variable_on_chain_schema: Vec::new(),539                const_on_chain_schema: Vec::new(),540            };541542            // Add new collection to map543            <Collection<T>>::insert(next_id, new_collection);544545            // call event546            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));547548            Ok(())549        }550551        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.552        /// 553        /// # Permissions554        /// 555        /// * Collection Owner.556        /// 557        /// # Arguments558        /// 559        /// * collection_id: collection to destroy.560        #[weight = T::WeightInfo::destroy_collection()]561        pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {562563            let sender = ensure_signed(origin)?;564            Self::check_owner_permissions(collection_id, sender)?;565566            <AddressTokens<T>>::remove_prefix(collection_id);567            <ApprovedList<T>>::remove_prefix(collection_id);568            <Balance<T>>::remove_prefix(collection_id);569            <ItemListIndex>::remove(collection_id);570            <AdminList<T>>::remove(collection_id);571            <Collection<T>>::remove(collection_id);572            <WhiteList<T>>::remove(collection_id);573574            <NftItemList<T>>::remove_prefix(collection_id);575            <FungibleItemList<T>>::remove_prefix(collection_id);576            <ReFungibleItemList<T>>::remove_prefix(collection_id);577578            <NftTransferBasket<T>>::remove_prefix(collection_id);579            <FungibleTransferBasket<T>>::remove_prefix(collection_id);580            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);581582            if CollectionCount::get() > 0583            {584                // bound couter585                let total = CollectionCount::get()586                    .checked_sub(1)587                    .ok_or(Error::<T>::NumOverflow)?;588589                CollectionCount::put(total);590            }591592            Ok(())593        }594595        /// Add an address to white list.596        /// 597        /// # Permissions598        /// 599        /// * Collection Owner600        /// * Collection Admin601        /// 602        /// # Arguments603        /// 604        /// * collection_id.605        /// 606        /// * address.607        #[weight = T::WeightInfo::add_to_white_list()]608        pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{609610            let sender = ensure_signed(origin)?;611            Self::check_owner_or_admin_permissions(collection_id, sender)?;612613            let mut white_list_collection: Vec<T::AccountId>;614            if <WhiteList<T>>::contains_key(collection_id) {615                white_list_collection = <WhiteList<T>>::get(collection_id);616                if !white_list_collection.contains(&address.clone())617                {618                    white_list_collection.push(address.clone());619                }620            }621            else {622                white_list_collection = Vec::new();623                white_list_collection.push(address.clone());624            }625626            <WhiteList<T>>::insert(collection_id, white_list_collection);627            Ok(())628        }629630        /// Remove an address from white list.631        /// 632        /// # Permissions633        /// 634        /// * Collection Owner635        /// * Collection Admin636        /// 637        /// # Arguments638        /// 639        /// * collection_id.640        /// 641        /// * address.642        #[weight = T::WeightInfo::remove_from_white_list()]643        pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{644645            let sender = ensure_signed(origin)?;646            Self::check_owner_or_admin_permissions(collection_id, sender)?;647648            if <WhiteList<T>>::contains_key(collection_id) {649                let mut white_list_collection = <WhiteList<T>>::get(collection_id);650                if white_list_collection.contains(&address.clone())651                {652                    white_list_collection.retain(|i| *i != address.clone());653                    <WhiteList<T>>::insert(collection_id, white_list_collection);654                }655            }656657            Ok(())658        }659660        /// Toggle between normal and white list access for the methods with access for `Anyone`.661        /// 662        /// # Permissions663        /// 664        /// * Collection Owner.665        /// 666        /// # Arguments667        /// 668        /// * collection_id.669        /// 670        /// * mode: [AccessMode]671        #[weight = T::WeightInfo::set_public_access_mode()]672        pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult673        {674            let sender = ensure_signed(origin)?;675676            Self::check_owner_permissions(collection_id, sender)?;677            let mut target_collection = <Collection<T>>::get(collection_id);678            target_collection.access = mode;679            <Collection<T>>::insert(collection_id, target_collection);680681            Ok(())682        }683684        /// Allows Anyone to create tokens if:685        /// * White List is enabled, and686        /// * Address is added to white list, and687        /// * This method was called with True parameter688        /// 689        /// # Permissions690        /// * Collection Owner691        ///692        /// # Arguments693        /// 694        /// * collection_id.695        /// 696        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.697        #[weight = T::WeightInfo::set_mint_permission()]698        pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult699        {700            let sender = ensure_signed(origin)?;701702            Self::check_owner_permissions(collection_id, sender)?;703            let mut target_collection = <Collection<T>>::get(collection_id);704            target_collection.mint_mode = mint_permission;705            <Collection<T>>::insert(collection_id, target_collection);706707            Ok(())708        }709710        /// Change the owner of the collection.711        /// 712        /// # Permissions713        /// 714        /// * Collection Owner.715        /// 716        /// # Arguments717        /// 718        /// * collection_id.719        /// 720        /// * new_owner.721        #[weight = T::WeightInfo::change_collection_owner()]722        pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {723724            let sender = ensure_signed(origin)?;725            Self::check_owner_permissions(collection_id, sender)?;726            let mut target_collection = <Collection<T>>::get(collection_id);727            target_collection.owner = new_owner;728            <Collection<T>>::insert(collection_id, target_collection);729730            Ok(())731        }732733        /// Adds an admin of the Collection.734        /// 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. 735        /// 736        /// # Permissions737        /// 738        /// * Collection Owner.739        /// * Collection Admin.740        /// 741        /// # Arguments742        /// 743        /// * collection_id: ID of the Collection to add admin for.744        /// 745        /// * new_admin_id: Address of new admin to add.746        #[weight = T::WeightInfo::add_collection_admin()]747        pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {748749            let sender = ensure_signed(origin)?;750            Self::check_owner_or_admin_permissions(collection_id, sender)?;751            let mut admin_arr: Vec<T::AccountId> = Vec::new();752753            if <AdminList<T>>::contains_key(collection_id)754            {755                admin_arr = <AdminList<T>>::get(collection_id);756                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);757            }758759            // Number of collection admins760            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);761762            admin_arr.push(new_admin_id);763            <AdminList<T>>::insert(collection_id, admin_arr);764765            Ok(())766        }767768        /// 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.769        ///770        /// # Permissions771        /// 772        /// * Collection Owner.773        /// * Collection Admin.774        /// 775        /// # Arguments776        /// 777        /// * collection_id: ID of the Collection to remove admin for.778        /// 779        /// * account_id: Address of admin to remove.780        #[weight = T::WeightInfo::remove_collection_admin()]781        pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {782783            let sender = ensure_signed(origin)?;784            Self::check_owner_or_admin_permissions(collection_id, sender)?;785786            if <AdminList<T>>::contains_key(collection_id)787            {788                let mut admin_arr = <AdminList<T>>::get(collection_id);789                admin_arr.retain(|i| *i != account_id);790                <AdminList<T>>::insert(collection_id, admin_arr);791            }792793            Ok(())794        }795796        /// # Permissions797        /// 798        /// * Collection Owner799        /// 800        /// # Arguments801        /// 802        /// * collection_id.803        /// 804        /// * new_sponsor.805        #[weight = T::WeightInfo::set_collection_sponsor()]806        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {807808            let sender = ensure_signed(origin)?;809            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);810811            let mut target_collection = <Collection<T>>::get(collection_id);812            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);813814            target_collection.unconfirmed_sponsor = new_sponsor;815            <Collection<T>>::insert(collection_id, target_collection);816817            Ok(())818        }819820        /// # Permissions821        /// 822        /// * Sponsor.823        /// 824        /// # Arguments825        /// 826        /// * collection_id.827        #[weight = T::WeightInfo::confirm_sponsorship()]828        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {829830            let sender = ensure_signed(origin)?;831            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);832833            let mut target_collection = <Collection<T>>::get(collection_id);834            ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);835836            target_collection.sponsor = target_collection.unconfirmed_sponsor;837            target_collection.unconfirmed_sponsor = T::AccountId::default();838            <Collection<T>>::insert(collection_id, target_collection);839840            Ok(())841        }842843        /// Switch back to pay-per-own-transaction model.844        ///845        /// # Permissions846        ///847        /// * Collection owner.848        /// 849        /// # Arguments850        /// 851        /// * collection_id.852        #[weight = T::WeightInfo::remove_collection_sponsor()]853        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {854855            let sender = ensure_signed(origin)?;856            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);857858            let mut target_collection = <Collection<T>>::get(collection_id);859            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);860861            target_collection.sponsor = T::AccountId::default();862            <Collection<T>>::insert(collection_id, target_collection);863864            Ok(())865        }866867        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.868        /// 869        /// # Permissions870        /// 871        /// * Collection Owner.872        /// * Collection Admin.873        /// * Anyone if874        ///     * White List is enabled, and875        ///     * Address is added to white list, and876        ///     * MintPermission is enabled (see SetMintPermission method)877        /// 878        /// # Arguments879        /// 880        /// * collection_id: ID of the collection.881        /// 882        /// * owner: Address, initial owner of the NFT.883        ///884        /// * data: Token data to store on chain.885        // #[weight =886        // (130_000_000 as Weight)887        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))888        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))889        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]890891        #[weight = T::WeightInfo::create_item(data.len())]892        pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {893894            let sender = ensure_signed(origin)?;895896            Self::collection_exists(collection_id)?;897898            let target_collection = <Collection<T>>::get(collection_id);899900            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;901            Self::validate_create_item_args(&target_collection, &data)?;902            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;903904            Ok(())905        }906907        /// This method creates multiple instances of NFT Collection created with CreateCollection method.908        /// 909        /// # Permissions910        /// 911        /// * Collection Owner.912        /// * Collection Admin.913        /// * Anyone if914        ///     * White List is enabled, and915        ///     * Address is added to white list, and916        ///     * MintPermission is enabled (see SetMintPermission method)917        /// 918        /// # Arguments919        /// 920        /// * collection_id: ID of the collection.921        /// 922        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].923        /// 924        /// * owner: Address, initial owner of the NFT.925        #[weight = T::WeightInfo::create_item(items_data.into_iter()926                               .map(|data| { data.len() })927                               .sum())]928        pub fn create_multiple_items(origin, collection_id: u64, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {929930            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);931            let sender = ensure_signed(origin)?;932933            Self::collection_exists(collection_id)?;934            let target_collection = <Collection<T>>::get(collection_id);935936            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;937938            for data in &items_data {939                Self::validate_create_item_args(&target_collection, data)?;940            }941            for data in &items_data {942                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;943            }944945            Ok(())946        }947948        /// Destroys a concrete instance of NFT.949        /// 950        /// # Permissions951        /// 952        /// * Collection Owner.953        /// * Collection Admin.954        /// * Current NFT Owner.955        /// 956        /// # Arguments957        /// 958        /// * collection_id: ID of the collection.959        /// 960        /// * item_id: ID of NFT to burn.961        #[weight = T::WeightInfo::burn_item()]962        pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {963964            let sender = ensure_signed(origin)?;965            Self::collection_exists(collection_id)?;966967            // Transfer permissions check968            let target_collection = <Collection<T>>::get(collection_id);969            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||970                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),971                Error::<T>::NoPermission);972973            if target_collection.access == AccessMode::WhiteList {974                Self::check_white_list(collection_id, &sender)?;975            }976977            match target_collection.mode978            {979                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,980                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,981                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,982                _ => ()983            };984985            // call event986            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));987988            Ok(())989        }990991        /// Change ownership of the token.992        /// 993        /// # Permissions994        /// 995        /// * Collection Owner996        /// * Collection Admin997        /// * Current NFT owner998        ///999        /// # Arguments1000        /// 1001        /// * recipient: Address of token recipient.1002        /// 1003        /// * collection_id.1004        /// 1005        /// * item_id: ID of the item1006        ///     * Non-Fungible Mode: Required.1007        ///     * Fungible Mode: Ignored.1008        ///     * Re-Fungible Mode: Required.1009        /// 1010        /// * value: Amount to transfer.1011        ///     * Non-Fungible Mode: Ignored1012        ///     * Fungible Mode: Must specify transferred amount1013        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1014        #[weight = T::WeightInfo::transfer()]1015        pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {10161017            let sender = ensure_signed(origin)?;10181019            // Transfer permissions check1020            let target_collection = <Collection<T>>::get(collection_id);1021            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1022                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1023                Error::<T>::NoPermission);10241025            if target_collection.access == AccessMode::WhiteList {1026                Self::check_white_list(collection_id, &sender)?;1027                Self::check_white_list(collection_id, &recipient)?;1028            }10291030            match target_collection.mode1031            {1032                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1033                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1034                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1035                _ => ()1036            };10371038            Ok(())1039        }10401041        /// Set, change, or remove approved address to transfer the ownership of the NFT.1042        /// 1043        /// # Permissions1044        /// 1045        /// * Collection Owner1046        /// * Collection Admin1047        /// * Current NFT owner1048        /// 1049        /// # Arguments1050        /// 1051        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1052        /// 1053        /// * collection_id.1054        /// 1055        /// * item_id: ID of the item.1056        #[weight = T::WeightInfo::approve()]1057        pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {10581059            let sender = ensure_signed(origin)?;10601061            // Transfer permissions check1062            let target_collection = <Collection<T>>::get(collection_id);1063            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1064                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1065                Error::<T>::NoPermission);10661067            if target_collection.access == AccessMode::WhiteList {1068                Self::check_white_list(collection_id, &sender)?;1069                Self::check_white_list(collection_id, &approved)?;1070            }10711072            // amount param stub1073            let amount = 100000000;10741075            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1076            if list_exists {10771078                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1079                let item_contains = list.iter().any(|i| i.approved == approved);10801081                if !item_contains {1082                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1083                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1084                }1085            } else {10861087                let mut list = Vec::new();1088                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1089                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1090            }10911092            Ok(())1093        }1094        1095        /// 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.1096        /// 1097        /// # Permissions1098        /// * Collection Owner1099        /// * Collection Admin1100        /// * Current NFT owner1101        /// * Address approved by current NFT owner1102        /// 1103        /// # Arguments1104        /// 1105        /// * from: Address that owns token.1106        /// 1107        /// * recipient: Address of token recipient.1108        /// 1109        /// * collection_id.1110        /// 1111        /// * item_id: ID of the item.1112        /// 1113        /// * value: Amount to transfer.1114        #[weight = T::WeightInfo::transfer_from()]1115        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {11161117            let sender = ensure_signed(origin)?;1118            let mut appoved_transfer = false;11191120            // Check approve1121            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1122                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1123                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1124                if opt_item.is_some()1125                {1126                    appoved_transfer = true;1127                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1128                }1129            }11301131            // Transfer permissions check1132            let target_collection = <Collection<T>>::get(collection_id);1133                ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1134                Error::<T>::NoPermission);11351136            if target_collection.access == AccessMode::WhiteList {1137                Self::check_white_list(collection_id, &sender)?;1138                Self::check_white_list(collection_id, &recipient)?;1139            }11401141            // remove approve1142            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1143                .into_iter().filter(|i| i.approved != sender.clone()).collect();1144            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);114511461147            match target_collection.mode1148            {1149                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1150                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1151                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1152                _ => ()1153            };11541155            Ok(())1156        }11571158        ///1159        #[weight = 0]1160        pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11611162            // let no_perm_mes = "You do not have permissions to modify this collection";1163            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1164            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1165            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11661167            // // on_nft_received  call11681169            // Self::transfer(origin, collection_id, item_id, new_owner)?;11701171            Ok(())1172        }11731174        /// Set off-chain data schema.1175        /// 1176        /// # Permissions1177        /// 1178        /// * Collection Owner1179        /// * Collection Admin1180        /// 1181        /// # Arguments1182        /// 1183        /// * collection_id.1184        /// 1185        /// * schema: String representing the offchain data schema.1186        #[weight = T::WeightInfo::set_variable_meta_data()]1187        pub fn set_variable_meta_data (1188            origin,1189            collection_id: u64,1190            item_id: u64,1191            data: Vec<u8>1192        ) -> DispatchResult {1193            let sender = ensure_signed(origin)?;1194            1195            Self::collection_exists(collection_id)?;1196            1197            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);11981199            // Modify permissions check1200            let target_collection = <Collection<T>>::get(collection_id);1201            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1202                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1203                Error::<T>::NoPermission);12041205            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12061207            match target_collection.mode1208            {1209                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1210                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1211                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1212                _ => fail!(Error::<T>::UnexpectedCollectionType)1213            };12141215            Ok(())1216        }1217        12181219        /// Set off-chain data schema.1220        /// 1221        /// # Permissions1222        /// 1223        /// * Collection Owner1224        /// * Collection Admin1225        /// 1226        /// # Arguments1227        /// 1228        /// * collection_id.1229        /// 1230        /// * schema: String representing the offchain data schema.1231        #[weight = T::WeightInfo::set_offchain_schema()]1232        pub fn set_offchain_schema(1233            origin,1234            collection_id: u64,1235            schema: Vec<u8>1236        ) -> DispatchResult {1237            let sender = ensure_signed(origin)?;1238            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12391240            let mut target_collection = <Collection<T>>::get(collection_id);1241            target_collection.offchain_schema = schema;1242            <Collection<T>>::insert(collection_id, target_collection);12431244            Ok(())1245        }12461247        /// Set const on-chain data schema.1248        /// 1249        /// # Permissions1250        /// 1251        /// * Collection Owner1252        /// * Collection Admin1253        /// 1254        /// # Arguments1255        /// 1256        /// * collection_id.1257        /// 1258        /// * schema: String representing the const on-chain data schema.1259        #[weight = T::WeightInfo::set_const_on_chain_schema()]1260        pub fn set_const_on_chain_schema (1261            origin,1262            collection_id: u64,1263            schema: Vec<u8>1264        ) -> DispatchResult {1265            let sender = ensure_signed(origin)?;1266            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12671268            let mut target_collection = <Collection<T>>::get(collection_id);1269            target_collection.const_on_chain_schema = schema;1270            <Collection<T>>::insert(collection_id, target_collection);12711272            Ok(())1273        }12741275        /// Set variable on-chain data schema.1276        /// 1277        /// # Permissions1278        /// 1279        /// * Collection Owner1280        /// * Collection Admin1281        /// 1282        /// # Arguments1283        /// 1284        /// * collection_id.1285        /// 1286        /// * schema: String representing the variable on-chain data schema.1287        #[weight = T::WeightInfo::set_const_on_chain_schema()]1288        pub fn set_variable_on_chain_schema (1289            origin,1290            collection_id: u64,1291            schema: Vec<u8>1292        ) -> DispatchResult {1293            let sender = ensure_signed(origin)?;1294            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12951296            let mut target_collection = <Collection<T>>::get(collection_id);1297            target_collection.variable_on_chain_schema = schema;1298            <Collection<T>>::insert(collection_id, target_collection);12991300            Ok(())1301        }13021303        // Sudo permissions function1304        #[weight = 0]1305        pub fn set_chain_limits(1306            origin,1307            limits: ChainLimits1308        ) -> DispatchResult {1309            ensure_root(origin)?;1310            <ChainLimit>::put(limits);1311            Ok(())1312        }13131314        /// Enable smart contract self-sponsoring.1315        /// 1316        /// # Permissions1317        /// 1318        /// * Contract Owner1319        /// 1320        /// # Arguments1321        /// 1322        /// * contract address1323        /// * enable flag1324        /// 1325        #[weight = T::WeightInfo::enable_contract_sponsoring()]1326        pub fn enable_contract_sponsoring(1327            origin,1328            contract_address: T::AccountId,1329            enable: bool1330        ) -> DispatchResult {13311332            let sender = ensure_signed(origin)?;13331334            #[cfg(feature = "runtime-benchmarks")]1335            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13361337            let mut is_owner = false;1338            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1339                let owner = <ContractOwner<T>>::get(&contract_address);1340                is_owner = sender == owner;1341            }1342            ensure!(is_owner, Error::<T>::NoPermission);13431344            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1345            Ok(())1346        }13471348        /// Set the rate limit for contract sponsoring to specified number of blocks.1349        /// 1350        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1351        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1352        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1353        /// from contract endowment if there are at least B blocks between such transactions. 1354        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1355        /// 1356        /// # Permissions1357        /// 1358        /// * Contract Owner1359        /// 1360        /// # Arguments1361        /// 1362        /// -`contract_address`: Address of the contract to sponsor1363        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1364        /// 1365        #[weight = 0]1366        pub fn set_contract_sponsoring_rate_limit(1367            origin,1368            contract_address: T::AccountId,1369            rate_limit: T::BlockNumber1370        ) -> DispatchResult {1371            let sender = ensure_signed(origin)?;1372            let mut is_owner = false;1373            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1374                let owner = <ContractOwner<T>>::get(&contract_address);1375                is_owner = sender == owner;1376            }1377            ensure!(is_owner, Error::<T>::NoPermission);13781379            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1380            Ok(())1381        }13821383        // #[cfg(feature = "runtime-benchmarks")]1384        // #[weight = 0]1385        // pub fn add_contract_sponsoring_debug(1386        //     origin,1387        //     contract_address: T::AccountId, 1388        //     owner: T::AccountId) -> DispatchResult {1389        //     let sender = ensure_signed(origin)?;1390        //     <ContractOwner<T>>::insert(contract_address.clone(), owner);1391        //     Ok(())1392        // }1393    1394    }1395}13961397impl<T: Trait> Module<T> {13981399    fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {14001401        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1402            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1403            Self::check_white_list(collection_id, owner)?;1404            Self::check_white_list(collection_id, sender)?;1405        }14061407        Ok(())1408    }14091410    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1411        match target_collection.mode1412        {1413            CollectionMode::NFT => {1414                if let CreateItemData::NFT(data) = data {1415                    // check sizes1416                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1417                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1418                } else {1419                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1420                }1421            },1422            CollectionMode::Fungible(_) => {1423                if let CreateItemData::Fungible(_) = data {1424                } else {1425                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1426                }1427            },1428            CollectionMode::ReFungible(_) => {1429                if let CreateItemData::ReFungible(data) = data {14301431                    // check sizes1432                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1433                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1434                } else {1435                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1436                }1437            },1438            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1439        };14401441        Ok(())1442    }14431444    fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1445        match data1446        {1447            CreateItemData::NFT(data) => {1448                let item = NftItemType {1449                    collection: collection_id,1450                    owner,1451                    const_data: data.const_data,1452                    variable_data: data.variable_data1453                };14541455                Self::add_nft_item(item)?;1456            },1457            CreateItemData::Fungible(_) => {1458                let item = FungibleItemType {1459                    collection: collection_id,1460                    owner,1461                    value: (10 as u128).pow(collection.decimal_points)1462                };14631464                Self::add_fungible_item(item)?;1465            },1466            CreateItemData::ReFungible(data) => {1467                let mut owner_list = Vec::new();1468                let value = (10 as u128).pow(collection.decimal_points);1469                owner_list.push(Ownership {owner: owner.clone(), fraction: value});14701471                let item = ReFungibleItemType {1472                    collection: collection_id,1473                    owner: owner_list,1474                    const_data: data.const_data,1475                    variable_data: data.variable_data1476                };14771478                Self::add_refungible_item(item)?;1479            }1480        };148114821483        // call event1484        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));14851486        Ok(())1487    }14881489    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1490        let current_index = <ItemListIndex>::get(item.collection)1491            .checked_add(1)1492            .ok_or(Error::<T>::NumOverflow)?;1493        let itemcopy = item.clone();1494        let owner = item.owner.clone();1495        let value = item.value as u64;14961497        Self::add_token_index(item.collection, current_index, owner.clone())?;14981499        <ItemListIndex>::insert(item.collection, current_index);1500        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15011502        // Add current block1503        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1504        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1505        1506        // Update balance1507        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1508            .checked_add(value)1509            .ok_or(Error::<T>::NumOverflow)?;1510        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15111512        Ok(())1513    }15141515    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1516        let current_index = <ItemListIndex>::get(item.collection)1517            .checked_add(1)1518            .ok_or(Error::<T>::NumOverflow)?;1519        let itemcopy = item.clone();15201521        let value = item.owner.first().unwrap().fraction as u64;1522        let owner = item.owner.first().unwrap().owner.clone();15231524        Self::add_token_index(item.collection, current_index, owner.clone())?;15251526        <ItemListIndex>::insert(item.collection, current_index);1527        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15281529        // Add current block1530        let block_number: T::BlockNumber = 0.into();1531        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);15321533        // Update balance1534        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1535            .checked_add(value)1536            .ok_or(Error::<T>::NumOverflow)?;1537        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15381539        Ok(())1540    }15411542    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1543        let current_index = <ItemListIndex>::get(item.collection)1544            .checked_add(1)1545            .ok_or(Error::<T>::NumOverflow)?;15461547        let item_owner = item.owner.clone();1548        let collection_id = item.collection.clone();1549        Self::add_token_index(collection_id, current_index, item.owner.clone())?;15501551        <ItemListIndex>::insert(collection_id, current_index);1552        <NftItemList<T>>::insert(collection_id, current_index, item);15531554        // Add current block1555        let block_number: T::BlockNumber = 0.into();1556        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);15571558        // Update balance1559        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1560            .checked_add(1)1561            .ok_or(Error::<T>::NumOverflow)?;1562        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);15631564        Ok(())1565    }15661567    fn burn_refungible_item(1568        collection_id: u64,1569        item_id: u64,1570        owner: T::AccountId,1571    ) -> DispatchResult {1572        ensure!(1573            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1574            Error::<T>::TokenNotFound1575        );1576        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1577        let item = collection1578            .owner1579            .iter()1580            .filter(|&i| i.owner == owner)1581            .next()1582            .unwrap();1583        Self::remove_token_index(collection_id, item_id, owner.clone())?;15841585        // remove approve list1586        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));15871588        // update balance1589        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1590            .checked_sub(item.fraction as u64)1591            .ok_or(Error::<T>::NumOverflow)?;1592        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15931594        <ReFungibleItemList<T>>::remove(collection_id, item_id);15951596        Ok(())1597    }15981599    fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1600        ensure!(1601            <NftItemList<T>>::contains_key(collection_id, item_id),1602            Error::<T>::TokenNotFound1603        );1604        let item = <NftItemList<T>>::get(collection_id, item_id);1605        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16061607        // remove approve list1608        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16091610        // update balance1611        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1612            .checked_sub(1)1613            .ok_or(Error::<T>::NumOverflow)?;1614        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1615        <NftItemList<T>>::remove(collection_id, item_id);16161617        Ok(())1618    }16191620    fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1621        ensure!(1622            <FungibleItemList<T>>::contains_key(collection_id, item_id),1623            Error::<T>::TokenNotFound1624        );1625        let item = <FungibleItemList<T>>::get(collection_id, item_id);1626        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16271628        // remove approve list1629        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16301631        // update balance1632        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1633            .checked_sub(item.value as u64)1634            .ok_or(Error::<T>::NumOverflow)?;1635        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);16361637        <FungibleItemList<T>>::remove(collection_id, item_id);16381639        Ok(())1640    }16411642    fn collection_exists(collection_id: u64) -> DispatchResult {1643        ensure!(1644            <Collection<T>>::contains_key(collection_id),1645            Error::<T>::CollectionNotFound1646        );1647        Ok(())1648    }16491650    fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1651        Self::collection_exists(collection_id)?;16521653        let target_collection = <Collection<T>>::get(collection_id);1654        ensure!(1655            subject == target_collection.owner,1656            Error::<T>::NoPermission1657        );16581659        Ok(())1660    }16611662    fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1663        let target_collection = <Collection<T>>::get(collection_id);1664        let mut result: bool = subject == target_collection.owner;1665        let exists = <AdminList<T>>::contains_key(collection_id);16661667        if !result & exists {1668            if <AdminList<T>>::get(collection_id).contains(&subject) {1669                result = true1670            }1671        }16721673        result1674    }16751676    fn check_owner_or_admin_permissions(1677        collection_id: u64,1678        subject: T::AccountId,1679    ) -> DispatchResult {1680        Self::collection_exists(collection_id)?;1681        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());16821683        ensure!(1684            result,1685            Error::<T>::NoPermission1686        );1687        Ok(())1688    }16891690    fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1691        let target_collection = <Collection<T>>::get(collection_id);16921693        match target_collection.mode {1694            CollectionMode::NFT => {1695                <NftItemList<T>>::get(collection_id, item_id).owner == subject1696            }1697            CollectionMode::Fungible(_) => {1698                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1699            }1700            CollectionMode::ReFungible(_) => {1701                <ReFungibleItemList<T>>::get(collection_id, item_id)1702                    .owner1703                    .iter()1704                    .any(|i| i.owner == subject)1705            }1706            CollectionMode::Invalid => false,1707        }1708    }17091710    fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1711        let mes = Error::<T>::AddresNotInWhiteList;1712        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1713        let wl = <WhiteList<T>>::get(collection_id);1714        ensure!(wl.contains(address), mes);17151716        Ok(())1717    }17181719    fn transfer_fungible(1720        collection_id: u64,1721        item_id: u64,1722        value: u64,1723        owner: T::AccountId,1724        new_owner: T::AccountId,1725    ) -> DispatchResult {1726        ensure!(1727            <FungibleItemList<T>>::contains_key(collection_id, item_id),1728            Error::<T>::TokenNotFound1729        );17301731        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1732        let amount = full_item.value;17331734        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);17351736        // update balance1737        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1738            .checked_sub(value)1739            .ok_or(Error::<T>::NumOverflow)?;1740        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);17411742        let mut new_owner_account_id = 0;1743        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1744        if new_owner_items.len() > 0 {1745            new_owner_account_id = new_owner_items[0];1746        }17471748        let val64 = value.into();17491750        // transfer1751        if amount == val64 && new_owner_account_id == 0 {1752            // change owner1753            // new owner do not have account1754            let mut new_full_item = full_item.clone();1755            new_full_item.owner = new_owner.clone();1756            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17571758            // update balance1759            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1760                .checked_add(value)1761                .ok_or(Error::<T>::NumOverflow)?;1762            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17631764            // update index collection1765            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1766        } else {1767            let mut new_full_item = full_item.clone();1768            new_full_item.value -= val64;17691770            // separate amount1771            if new_owner_account_id > 0 {1772                // new owner has account1773                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1774                item.value += val64;17751776                // update balance1777                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1778                    .checked_add(value)1779                    .ok_or(Error::<T>::NumOverflow)?;1780                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17811782                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1783            } else {1784                // new owner do not have account1785                let item = FungibleItemType {1786                    collection: collection_id,1787                    owner: new_owner.clone(),1788                    value: val64,1789                };17901791                Self::add_fungible_item(item)?;1792            }17931794            if amount == val64 {1795                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;17961797                // remove approve list1798                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1799                <FungibleItemList<T>>::remove(collection_id, item_id);1800            }18011802            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1803        }18041805        Ok(())1806    }18071808    fn transfer_refungible(1809        collection_id: u64,1810        item_id: u64,1811        value: u64,1812        owner: T::AccountId,1813        new_owner: T::AccountId,1814    ) -> DispatchResult {1815        ensure!(1816            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1817            Error::<T>::TokenNotFound1818        );18191820        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1821        let item = full_item1822            .owner1823            .iter()1824            .filter(|i| i.owner == owner)1825            .next()1826            .ok_or(Error::<T>::NumOverflow)?;1827        let amount = item.fraction;18281829        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);18301831        // update balance1832        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1833            .checked_sub(value)1834            .ok_or(Error::<T>::NumOverflow)?;1835        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18361837        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1838            .checked_add(value)1839            .ok_or(Error::<T>::NumOverflow)?;1840        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18411842        let old_owner = item.owner.clone();1843        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1844        let val64 = value.into();18451846        // transfer1847        if amount == val64 && !new_owner_has_account {1848            // change owner1849            // new owner do not have account1850            let mut new_full_item = full_item.clone();1851            new_full_item1852                .owner1853                .iter_mut()1854                .find(|i| i.owner == owner)1855                .unwrap()1856                .owner = new_owner.clone();1857            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18581859            // update index collection1860            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1861        } else {1862            let mut new_full_item = full_item.clone();1863            new_full_item1864                .owner1865                .iter_mut()1866                .find(|i| i.owner == owner)1867                .unwrap()1868                .fraction -= val64;18691870            // separate amount1871            if new_owner_has_account {1872                // new owner has account1873                new_full_item1874                    .owner1875                    .iter_mut()1876                    .find(|i| i.owner == new_owner)1877                    .unwrap()1878                    .fraction += val64;1879            } else {1880                // new owner do not have account1881                new_full_item.owner.push(Ownership {1882                    owner: new_owner.clone(),1883                    fraction: val64,1884                });1885                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1886            }18871888            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1889        }18901891        Ok(())1892    }18931894    fn transfer_nft(1895        collection_id: u64,1896        item_id: u64,1897        sender: T::AccountId,1898        new_owner: T::AccountId,1899    ) -> DispatchResult {1900        ensure!(1901            <NftItemList<T>>::contains_key(collection_id, item_id),1902            Error::<T>::TokenNotFound1903        );19041905        let mut item = <NftItemList<T>>::get(collection_id, item_id);19061907        ensure!(1908            sender == item.owner,1909            Error::<T>::MustBeTokenOwner1910        );19111912        // update balance1913        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1914            .checked_sub(1)1915            .ok_or(Error::<T>::NumOverflow)?;1916        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19171918        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1919            .checked_add(1)1920            .ok_or(Error::<T>::NumOverflow)?;1921        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19221923        // change owner1924        let old_owner = item.owner.clone();1925        item.owner = new_owner.clone();1926        <NftItemList<T>>::insert(collection_id, item_id, item);19271928        // update index collection1929        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;19301931        // reset approved list1932        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1933        Ok(())1934    }1935    1936    fn item_exists(1937        collection_id: u64,1938        item_id: u64,1939        mode: &CollectionMode1940    ) -> DispatchResult {1941        match mode {1942            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1943            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1944            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1945            _ => ()1946        };1947        1948        Ok(())1949    }19501951    fn set_re_fungible_variable_data(1952        collection_id: u64,1953        item_id: u64,1954        data: Vec<u8>1955    ) -> DispatchResult {1956        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);19571958        item.variable_data = data;19591960        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);19611962        Ok(())1963    }19641965    fn set_nft_variable_data(1966        collection_id: u64,1967        item_id: u64,1968        data: Vec<u8>1969    ) -> DispatchResult {1970        let mut item = <NftItemList<T>>::get(collection_id, item_id);1971        1972        item.variable_data = data;19731974        <NftItemList<T>>::insert(collection_id, item_id, item);1975        1976        Ok(())1977    }19781979    fn init_collection(item: &CollectionType<T::AccountId>) {1980        // check params1981        assert!(1982            item.decimal_points <= 4,1983            "decimal_points parameter must be lower than 4"1984        );1985        assert!(1986            item.name.len() <= 64,1987            "Collection name can not be longer than 63 char"1988        );1989        assert!(1990            item.name.len() <= 256,1991            "Collection description can not be longer than 255 char"1992        );1993        assert!(1994            item.token_prefix.len() <= 16,1995            "Token prefix can not be longer than 15 char"1996        );19971998        // Generate next collection ID1999        let next_id = CreatedCollectionCount::get()2000            .checked_add(1)2001            .unwrap();20022003        CreatedCollectionCount::put(next_id);2004    }20052006    fn init_nft_token(item: &NftItemType<T::AccountId>) {2007        let current_index = <ItemListIndex>::get(item.collection)2008            .checked_add(1)2009            .unwrap();20102011        let item_owner = item.owner.clone();2012        let collection_id = item.collection.clone();2013        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();20142015        <ItemListIndex>::insert(collection_id, current_index);20162017        // Update balance2018        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2019            .checked_add(1)2020            .unwrap();2021        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2022    }20232024    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2025        let current_index = <ItemListIndex>::get(item.collection)2026            .checked_add(1)2027            .unwrap();2028        let owner = item.owner.clone();2029        let value = item.value as u64;20302031        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20322033        <ItemListIndex>::insert(item.collection, current_index);20342035        // Update balance2036        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2037            .checked_add(value)2038            .unwrap();2039        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2040    }20412042    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2043        let current_index = <ItemListIndex>::get(item.collection)2044            .checked_add(1)2045            .unwrap();20462047        let value = item.owner.first().unwrap().fraction as u64;2048        let owner = item.owner.first().unwrap().owner.clone();20492050        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20512052        <ItemListIndex>::insert(item.collection, current_index);20532054        // Update balance2055        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2056            .checked_add(value)2057            .unwrap();2058        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2059    }20602061    fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {20622063        // add to account limit2064        if <AccountItemCount<T>>::contains_key(owner.clone()) {20652066            // bound Owned tokens by a single address2067            let count = <AccountItemCount<T>>::get(owner.clone());2068            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);20692070            <AccountItemCount<T>>::insert(owner.clone(), count2071                .checked_add(1)2072                .ok_or(Error::<T>::NumOverflow)?);2073        }2074        else {2075            <AccountItemCount<T>>::insert(owner.clone(), 1);2076        }20772078        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2079        if list_exists {2080            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2081            let item_contains = list.contains(&item_index.clone());20822083            if !item_contains {2084                list.push(item_index.clone());2085            }20862087            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2088        } else {2089            let mut itm = Vec::new();2090            itm.push(item_index.clone());2091            <AddressTokens<T>>::insert(collection_id, owner, itm);2092            2093        }20942095        Ok(())2096    }20972098    fn remove_token_index(2099        collection_id: u64,2100        item_index: u64,2101        owner: T::AccountId,2102    ) -> DispatchResult {21032104        // update counter2105        <AccountItemCount<T>>::insert(owner.clone(), 2106            <AccountItemCount<T>>::get(owner.clone())2107            .checked_sub(1)2108            .ok_or(Error::<T>::NumOverflow)?);210921102111        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2112        if list_exists {2113            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2114            let item_contains = list.contains(&item_index.clone());21152116            if item_contains {2117                list.retain(|&item| item != item_index);2118                <AddressTokens<T>>::insert(collection_id, owner, list);2119            }2120        }21212122        Ok(())2123    }21242125    fn move_token_index(2126        collection_id: u64,2127        item_index: u64,2128        old_owner: T::AccountId,2129        new_owner: T::AccountId,2130    ) -> DispatchResult {2131        Self::remove_token_index(collection_id, item_index, old_owner)?;2132        Self::add_token_index(collection_id, item_index, new_owner)?;21332134        Ok(())2135    }2136}21372138////////////////////////////////////////////////////////////////////////////////////////////////////2139// Economic models2140// #region21412142/// Fee multiplier.2143pub type Multiplier = FixedU128;21442145type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2146    <T as system::Trait>::AccountId,2147>>::Balance;2148type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2149    <T as system::Trait>::AccountId,2150>>::NegativeImbalance;21512152/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2153/// in the queue.2154#[derive(Encode, Decode, Clone, Eq, PartialEq)]2155pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2156    #[codec(compact)] BalanceOf<T>2157);21582159impl<T: Trait + Send + Sync> sp_std::fmt::Debug2160    for ChargeTransactionPayment<T>2161{2162    #[cfg(feature = "std")]2163    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2164        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2165    }2166    #[cfg(not(feature = "std"))]2167    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2168        Ok(())2169    }2170}21712172impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2173where2174    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2175    BalanceOf<T>: Send + Sync + FixedPointOperand,2176{2177    /// utility constructor. Used only in client/factory code.2178    pub fn from(fee: BalanceOf<T>) -> Self {2179        Self(fee)2180    }21812182    pub fn traditional_fee(2183        len: usize,2184        info: &DispatchInfoOf<T::Call>,2185        tip: BalanceOf<T>,2186    ) -> BalanceOf<T>2187    where2188        T::Call: Dispatchable<Info = DispatchInfo>,2189    {2190        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2191    }21922193    fn withdraw_fee(2194        &self,2195        who: &T::AccountId,2196        call: &T::Call,2197        info: &DispatchInfoOf<T::Call>,2198        len: usize,2199    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2200        let tip = self.0;22012202        // Set fee based on call type. Creating collection costs 1 Unique.2203        // All other transactions have traditional fees so far2204        // let fee = match call.is_sub_type() {2205        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2206        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2207        //                                                 // _ => <BalanceOf<T>>::from(100)2208        // };2209        let fee = Self::traditional_fee(len, info, tip);22102211        // Determine who is paying transaction fee based on ecnomic model2212        // Parse call to extract collection ID and access collection sponsor2213        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2214            Some(Call::create_item(collection_id, _properties, _owner)) => {2215                <Collection<T>>::get(collection_id).sponsor2216            }2217            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2218                let _collection_mode = <Collection<T>>::get(collection_id).mode;22192220                // sponsor timeout2221                let sponsor_transfer = match _collection_mode {2222                    CollectionMode::NFT => {2223                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2224                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2225                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2226                        if block_number >= limit_time {2227                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2228                            true2229                        }2230                        else {2231                            false2232                        }2233                    }2234                    CollectionMode::Fungible(_) => {2235                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2236                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2237                        if basket.iter().any(|i| i.address == _new_owner.clone())2238                        {2239                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2240                            let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2241                            if block_number >= limit_time {2242                                basket.retain(|x| x.address == item.address);2243                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2244                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2245                                true2246                            }2247                            else {2248                                false2249                            }2250                        }2251                        else {2252                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2253                            true2254                        }2255                    }2256                    CollectionMode::ReFungible(_) => {2257                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2258                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2259                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2260                        if block_number >= limit_time {2261                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2262                            true2263                        } else {2264                            false2265                        }2266                    }2267                    _ => {2268                        false2269                    },2270                };22712272                if !sponsor_transfer {2273                    T::AccountId::default()2274                } else {2275                    <Collection<T>>::get(collection_id).sponsor2276                }2277            }22782279            _ => T::AccountId::default(),2280        };22812282        // Sponsor smart contracts2283        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {22842285            // On instantiation: set the contract owner2286            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {22872288                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2289                    code_hash,2290                    &data,2291                    &who,2292                );2293                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());22942295                T::AccountId::default()2296            },22972298            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2299            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {23002301                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());23022303                let mut sponsor_transfer = false;2304                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2305                    let last_tx_block = <ContractSponsorBasket<T>>::get(&called_contract);2306                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2307                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2308                    let limit_time = last_tx_block + rate_limit;23092310                    if block_number >= limit_time {2311                        <ContractSponsorBasket<T>>::insert(called_contract.clone(), block_number);2312                        sponsor_transfer = true;2313                    }2314                } else {2315                    sponsor_transfer = false;2316                }2317               2318                2319                let mut sp = T::AccountId::default();2320                if sponsor_transfer {2321                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2322                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2323                            sp = called_contract;2324                        }2325                    }2326                }23272328                sp2329            },23302331            _ => sponsor,2332        };23332334        let mut who_pays_fee: T::AccountId = sponsor.clone();2335        if sponsor == T::AccountId::default() {2336            who_pays_fee = who.clone();2337        }23382339        // Only mess with balances if fee is not zero.2340        if fee.is_zero() {2341            return Ok((fee, None));2342        }23432344        match <T as transaction_payment::Trait>::Currency::withdraw(2345            &who_pays_fee,2346            fee,2347            if tip.is_zero() {2348                WithdrawReason::TransactionPayment.into()2349            } else {2350                WithdrawReason::TransactionPayment | WithdrawReason::Tip2351            },2352            ExistenceRequirement::KeepAlive,2353        ) {2354            Ok(imbalance) => Ok((fee, Some(imbalance))),2355            Err(_) => Err(InvalidTransaction::Payment.into()),2356        }2357    }2358}235923602361impl<T: Trait + Send + Sync> SignedExtension2362    for ChargeTransactionPayment<T>2363where2364    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2365    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2366{2367    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2368    type AccountId = T::AccountId;2369    type Call = T::Call;2370    type AdditionalSigned = ();2371    type Pre = (2372        BalanceOf<T>,2373        Self::AccountId,2374        Option<NegativeImbalanceOf<T>>,2375        BalanceOf<T>,2376    );2377    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2378        Ok(())2379    }23802381    fn validate(2382        &self,2383        _who: &Self::AccountId,2384        _call: &Self::Call,2385        _info: &DispatchInfoOf<Self::Call>,2386        _len: usize,2387    ) -> TransactionValidity {2388        Ok(ValidTransaction::default())2389    }23902391    fn pre_dispatch(2392        self,2393        who: &Self::AccountId,2394        call: &Self::Call,2395        info: &DispatchInfoOf<Self::Call>,2396        len: usize,2397    ) -> Result<Self::Pre, TransactionValidityError> {2398        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2399        Ok((self.0, who.clone(), imbalance, fee))2400    }24012402    fn post_dispatch(2403        pre: Self::Pre,2404        info: &DispatchInfoOf<Self::Call>,2405        post_info: &PostDispatchInfoOf<Self::Call>,2406        len: usize,2407        _result: &DispatchResult,2408    ) -> Result<(), TransactionValidityError> {2409        let (tip, who, imbalance, fee) = pre;2410        if let Some(payed) = imbalance {2411            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2412                len as u32, info, post_info, tip,2413            );2414            let refund = fee.saturating_sub(actual_fee);2415            let actual_payment =2416                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2417                    &who, refund,2418                ) {2419                    Ok(refund_imbalance) => {2420                        // The refund cannot be larger than the up front payed max weight.2421                        // `PostDispatchInfo::calc_unspent` guards against such a case.2422                        match payed.offset(refund_imbalance) {2423                            Ok(actual_payment) => actual_payment,2424                            Err(_) => return Err(InvalidTransaction::Payment.into()),2425                        }2426                    }2427                    // We do not recreate the account using the refund. The up front payment2428                    // is gone in that case.2429                    Err(_) => payed,2430                };2431            let imbalances = actual_payment.split(tip);2432            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2433                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2434            );2435        }2436        Ok(())2437    }2438}24392440// #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 limits: CollectionLimits, // Collection private restrictions 113    pub variable_on_chain_schema: Vec<u8>, //114    pub const_on_chain_schema: Vec<u8>, //115}116117#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]118#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]119pub struct NftItemType<AccountId> {120    pub collection: u64,121    pub owner: AccountId,122    pub const_data: Vec<u8>,123    pub variable_data: Vec<u8>,124}125126#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub struct FungibleItemType<AccountId> {129    pub collection: u64,130    pub owner: AccountId,131    pub value: u128,132}133134#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]135#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]136pub struct ReFungibleItemType<AccountId> {137    pub collection: u64,138    pub owner: Vec<Ownership<AccountId>>,139    pub const_data: Vec<u8>,140    pub variable_data: Vec<u8>,141}142143#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]144#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]145pub struct ApprovePermissions<AccountId> {146    pub approved: AccountId,147    pub amount: u64,148}149150#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]151#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]152pub struct VestingItem<AccountId, Moment> {153    pub sender: AccountId,154    pub recipient: AccountId,155    pub collection_id: u64,156    pub item_id: u64,157    pub amount: u64,158    pub vesting_date: Moment,159}160161#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]162#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]163pub struct BasketItem<AccountId, BlockNumber> {164    pub address: AccountId,165    pub start_block: BlockNumber,166}167168#[derive(Encode, Decode, Debug, Clone, PartialEq)]169#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170pub struct CollectionLimits {171    pub account_token_ownership_limit: u32,172    pub sponsored_data_size: u32,173    pub token_limit: u32,174175    // Timeouts for item types in passed blocks176    pub sponsor_transfer_timeout: u32,177}178179impl Default for CollectionLimits {180    fn default() -> CollectionLimits {181        CollectionLimits { 182            account_token_ownership_limit: 0, 183            token_limit: 0,184            sponsored_data_size: 0, 185            sponsor_transfer_timeout: 0 }186    }187}188189#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]190#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]191pub struct ChainLimits {192    pub collection_numbers_limit: u64,193    pub account_token_ownership_limit: u64,194    pub collections_admins_limit: u64,195    pub custom_data_limit: u32,196197    // Timeouts for item types in passed blocks198    pub nft_sponsor_transfer_timeout: u32,199    pub fungible_sponsor_transfer_timeout: u32,200    pub refungible_sponsor_transfer_timeout: u32,201}202203pub trait WeightInfo {204	fn create_collection() -> Weight;205	fn destroy_collection() -> Weight;206	fn add_to_white_list() -> Weight;207	fn remove_from_white_list() -> Weight;208    fn set_public_access_mode() -> Weight;209    fn set_mint_permission() -> Weight;210    fn change_collection_owner() -> Weight;211    fn add_collection_admin() -> Weight;212    fn remove_collection_admin() -> Weight;213    fn set_collection_sponsor() -> Weight;214    fn confirm_sponsorship() -> Weight;215    fn remove_collection_sponsor() -> Weight;216    fn create_item(s: usize) -> Weight;217    fn burn_item() -> Weight;218    fn transfer() -> Weight;219    fn approve() -> Weight;220    fn transfer_from() -> Weight;221    fn set_offchain_schema() -> Weight;222    fn set_const_on_chain_schema() -> Weight;223    fn set_variable_on_chain_schema() -> Weight;224    fn set_variable_meta_data() -> Weight;225    fn enable_contract_sponsoring() -> Weight;226}227228#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]229#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]230pub struct CreateNftData {231    pub const_data: Vec<u8>,232    pub variable_data: Vec<u8>,233}234235#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]236#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]237pub struct CreateFungibleData {238}239240#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]241#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]242pub struct CreateReFungibleData {243    pub const_data: Vec<u8>,244    pub variable_data: Vec<u8>,245}246247#[derive(Encode, Decode, Debug, Clone, PartialEq)]248#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]249pub enum CreateItemData {250    NFT(CreateNftData),251    Fungible(CreateFungibleData),252    ReFungible(CreateReFungibleData)253}254255impl CreateItemData {256    pub fn len(&self) -> usize {257        let len = match self {258            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),259            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),260            _ => 0261        };262        263        return len;264    }265}266267impl From<CreateNftData> for CreateItemData {268    fn from(item: CreateNftData) -> Self {269        CreateItemData::NFT(item)270    }271}272273impl From<CreateReFungibleData> for CreateItemData {274    fn from(item: CreateReFungibleData) -> Self {275        CreateItemData::ReFungible(item)276    }277}278279impl From<CreateFungibleData> for CreateItemData {280    fn from(item: CreateFungibleData) -> Self {281        CreateItemData::Fungible(item)282    }283}284285286decl_error! {287	/// Error for non-fungible-token module.288	pub enum Error for Module<T: Trait> {289        /// Total collections bound exceeded.290        TotalCollectionsLimitExceeded,291		/// Decimal_points parameter must be lower than 4.292        CollectionDecimalPointLimitExceeded, 293        /// Collection name can not be longer than 63 char.294        CollectionNameLimitExceeded, 295        /// Collection description can not be longer than 255 char.296        CollectionDescriptionLimitExceeded, 297        /// Token prefix can not be longer than 15 char.298        CollectionTokenPrefixLimitExceeded,299        /// This collection does not exist.300        CollectionNotFound,301        /// Item not exists.302        TokenNotFound,303        /// Arithmetic calculation overflow.304        NumOverflow,       305        /// Account already has admin role.306        AlreadyAdmin,  307        /// You do not own this collection.308        NoPermission,309        /// This address is not set as sponsor, use setCollectionSponsor first.310        ConfirmUnsetSponsorFail,311        /// Collection is not in mint mode.312        PublicMintingNotAllowed,313        /// Sender parameter and item owner must be equal.314        MustBeTokenOwner,315        /// Item balance not enough.316        TokenValueTooLow,317        /// Size of item is too large.318        NftSizeLimitExceeded,319        /// No approve found320        ApproveNotFound,321        /// Requested value more than approved.322        TokenValueNotEnough,323        /// Only approved addresses can call this method.324        ApproveRequired,325        /// Address is not in white list.326        AddresNotInWhiteList,327        /// Number of collection admins bound exceeded.328        CollectionAdminsLimitExceeded,329        /// Owned tokens by a single address bound exceeded.330        AddressOwnershipLimitExceeded,331        /// Length of items properties must be greater than 0.332        EmptyArgument,333        /// const_data exceeded data limit.334        TokenConstDataLimitExceeded,335        /// variable_data exceeded data limit.336        TokenVariableDataLimitExceeded,337        /// Not NFT item data used to mint in NFT collection.338        NotNftDataUsedToMintNftCollectionToken,339        /// Not Fungible item data used to mint in Fungible collection.340        NotFungibleDataUsedToMintFungibleCollectionToken,341        /// Not Re Fungible item data used to mint in Re Fungible collection.342        NotReFungibleDataUsedToMintReFungibleCollectionToken,343        /// Unexpected collection type.344        UnexpectedCollectionType,345        /// Can't store metadata in fungible tokens.346        CantStoreMetadataInFungibleTokens,347        /// Collection token limit exceeded348        CollectionTokenLimitExceeded,349        /// Account token limit exceeded per collection350        AccountTokenLimitExceeded351	}352}353354pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {355    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;356357    /// Weight information for extrinsics in this pallet.358	type WeightInfo: WeightInfo;359}360361#[cfg(feature = "runtime-benchmarks")]362mod benchmarking;363364// #endregion365366decl_storage! {367    trait Store for Module<T: Trait> as Nft {368369        // Private members370        NextCollectionID: u64;371        CreatedCollectionCount: u64;372        ChainVersion: u64;373        ItemListIndex: map hasher(blake2_128_concat) u64 => u64;374375        // Chain limits struct376        pub ChainLimit get(fn chain_limit) config(): ChainLimits;377378        // Bound counters379        CollectionCount: u64;380        pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;381382        // Basic collections383        pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;384        pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;385        pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;386387        /// Balance owner per collection map388        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;389390        /// second parameter: item id + owner account id391        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;392393        /// Item collections394        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;395        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;396        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;397398        /// Index list399        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;400401        /// Tokens transfer baskets402        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;403        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>>;404        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;405406        // Contract Sponsorship and Ownership407        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;408        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;409        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;410        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;411    }412    add_extra_genesis {413        build(|config: &GenesisConfig<T>| {414            // Modification of storage415            for (_num, _c) in &config.collection {416                <Module<T>>::init_collection(_c);417            }418419            for (_num, _q, _i) in &config.nft_item_id {420                <Module<T>>::init_nft_token(_i);421            }422423            for (_num, _q, _i) in &config.fungible_item_id {424                <Module<T>>::init_fungible_token(_i);425            }426427            for (_num, _q, _i) in &config.refungible_item_id {428                <Module<T>>::init_refungible_token(_i);429            }430        })431    }432}433434decl_event!(435    pub enum Event<T>436    where437        AccountId = <T as system::Trait>::AccountId,438    {439        /// New collection was created440        /// 441        /// # Arguments442        /// 443        /// * collection_id: Globally unique identifier of newly created collection.444        /// 445        /// * mode: [CollectionMode] converted into u8.446        /// 447        /// * account_id: Collection owner.448        Created(u64, u8, AccountId),449450        /// New item was created.451        /// 452        /// # Arguments453        /// 454        /// * collection_id: Id of the collection where item was created.455        /// 456        /// * item_id: Id of an item. Unique within the collection.457        ItemCreated(u64, u64),458459        /// Collection item was burned.460        /// 461        /// # Arguments462        /// 463        /// collection_id.464        /// 465        /// item_id: Identifier of burned NFT.466        ItemDestroyed(u64, u64),467    }468);469470decl_module! {471    pub struct Module<T: Trait> for enum Call where origin: T::Origin {472473        fn deposit_event() = default;474        type Error = Error<T>;475476        fn on_initialize(now: T::BlockNumber) -> Weight {477478            if ChainVersion::get() < 2479            {480                let value = NextCollectionID::get();481                CreatedCollectionCount::put(value);482                ChainVersion::put(2);483            }484485            0486        }487488        /// 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.489        /// 490        /// # Permissions491        /// 492        /// * Anyone.493        /// 494        /// # Arguments495        /// 496        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.497        /// 498        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.499        /// 500        /// * token_prefix: UTF-8 string with token prefix.501        /// 502        /// * mode: [CollectionMode] collection type and type dependent data.503        // returns collection ID504        #[weight = T::WeightInfo::create_collection()]505        pub fn create_collection(origin,506                                 collection_name: Vec<u16>,507                                 collection_description: Vec<u16>,508                                 token_prefix: Vec<u8>,509                                 mode: CollectionMode) -> DispatchResult {510511            // Anyone can create a collection512            let who = ensure_signed(origin)?;513514            let decimal_points = match mode {515                CollectionMode::Fungible(points) => points,516                CollectionMode::ReFungible(points) => points,517                _ => 0518            };519520            // bound Total number of collections521            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);522523            // check params524            ensure!(decimal_points <= 4, Error::<T>::CollectionDecimalPointLimitExceeded);525526            let mut name = collection_name.to_vec();527            name.push(0);528            ensure!(name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);529530            let mut description = collection_description.to_vec();531            description.push(0);532            ensure!(name.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);533534            let mut prefix = token_prefix.to_vec();535            prefix.push(0);536            ensure!(prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);537538            // Generate next collection ID539            let next_id = CreatedCollectionCount::get()540                .checked_add(1)541                .ok_or(Error::<T>::NumOverflow)?;542543            // bound counter544            let total = CollectionCount::get()545                .checked_add(1)546                .ok_or(Error::<T>::NumOverflow)?;547548            CreatedCollectionCount::put(next_id);549            CollectionCount::put(total);550551            // Create new collection552            let new_collection = CollectionType {553                owner: who.clone(),554                name: name,555                mode: mode.clone(),556                mint_mode: false,557                access: AccessMode::Normal,558                description: description,559                decimal_points: decimal_points,560                token_prefix: prefix,561                offchain_schema: Vec::new(),562                sponsor: T::AccountId::default(),563                unconfirmed_sponsor: T::AccountId::default(),564                variable_on_chain_schema: Vec::new(),565                const_on_chain_schema: Vec::new(),566                limits: CollectionLimits::default(),567            };568569            // Add new collection to map570            <Collection<T>>::insert(next_id, new_collection);571572            // call event573            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));574575            Ok(())576        }577578        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.579        /// 580        /// # Permissions581        /// 582        /// * Collection Owner.583        /// 584        /// # Arguments585        /// 586        /// * collection_id: collection to destroy.587        #[weight = T::WeightInfo::destroy_collection()]588        pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {589590            let sender = ensure_signed(origin)?;591            Self::check_owner_permissions(collection_id, sender)?;592593            <AddressTokens<T>>::remove_prefix(collection_id);594            <ApprovedList<T>>::remove_prefix(collection_id);595            <Balance<T>>::remove_prefix(collection_id);596            <ItemListIndex>::remove(collection_id);597            <AdminList<T>>::remove(collection_id);598            <Collection<T>>::remove(collection_id);599            <WhiteList<T>>::remove(collection_id);600601            <NftItemList<T>>::remove_prefix(collection_id);602            <FungibleItemList<T>>::remove_prefix(collection_id);603            <ReFungibleItemList<T>>::remove_prefix(collection_id);604605            <NftTransferBasket<T>>::remove_prefix(collection_id);606            <FungibleTransferBasket<T>>::remove_prefix(collection_id);607            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);608609            if CollectionCount::get() > 0610            {611                // bound couter612                let total = CollectionCount::get()613                    .checked_sub(1)614                    .ok_or(Error::<T>::NumOverflow)?;615616                CollectionCount::put(total);617            }618619            Ok(())620        }621622        /// Add an address to white list.623        /// 624        /// # Permissions625        /// 626        /// * Collection Owner627        /// * Collection Admin628        /// 629        /// # Arguments630        /// 631        /// * collection_id.632        /// 633        /// * address.634        #[weight = T::WeightInfo::add_to_white_list()]635        pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{636637            let sender = ensure_signed(origin)?;638            Self::check_owner_or_admin_permissions(collection_id, sender)?;639640            let mut white_list_collection: Vec<T::AccountId>;641            if <WhiteList<T>>::contains_key(collection_id) {642                white_list_collection = <WhiteList<T>>::get(collection_id);643                if !white_list_collection.contains(&address.clone())644                {645                    white_list_collection.push(address.clone());646                }647            }648            else {649                white_list_collection = Vec::new();650                white_list_collection.push(address.clone());651            }652653            <WhiteList<T>>::insert(collection_id, white_list_collection);654            Ok(())655        }656657        /// Remove an address from white list.658        /// 659        /// # Permissions660        /// 661        /// * Collection Owner662        /// * Collection Admin663        /// 664        /// # Arguments665        /// 666        /// * collection_id.667        /// 668        /// * address.669        #[weight = T::WeightInfo::remove_from_white_list()]670        pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{671672            let sender = ensure_signed(origin)?;673            Self::check_owner_or_admin_permissions(collection_id, sender)?;674675            if <WhiteList<T>>::contains_key(collection_id) {676                let mut white_list_collection = <WhiteList<T>>::get(collection_id);677                if white_list_collection.contains(&address.clone())678                {679                    white_list_collection.retain(|i| *i != address.clone());680                    <WhiteList<T>>::insert(collection_id, white_list_collection);681                }682            }683684            Ok(())685        }686687        /// Toggle between normal and white list access for the methods with access for `Anyone`.688        /// 689        /// # Permissions690        /// 691        /// * Collection Owner.692        /// 693        /// # Arguments694        /// 695        /// * collection_id.696        /// 697        /// * mode: [AccessMode]698        #[weight = T::WeightInfo::set_public_access_mode()]699        pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult700        {701            let sender = ensure_signed(origin)?;702703            Self::check_owner_permissions(collection_id, sender)?;704            let mut target_collection = <Collection<T>>::get(collection_id);705            target_collection.access = mode;706            <Collection<T>>::insert(collection_id, target_collection);707708            Ok(())709        }710711        /// Allows Anyone to create tokens if:712        /// * White List is enabled, and713        /// * Address is added to white list, and714        /// * This method was called with True parameter715        /// 716        /// # Permissions717        /// * Collection Owner718        ///719        /// # Arguments720        /// 721        /// * collection_id.722        /// 723        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.724        #[weight = T::WeightInfo::set_mint_permission()]725        pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult726        {727            let sender = ensure_signed(origin)?;728729            Self::check_owner_permissions(collection_id, sender)?;730            let mut target_collection = <Collection<T>>::get(collection_id);731            target_collection.mint_mode = mint_permission;732            <Collection<T>>::insert(collection_id, target_collection);733734            Ok(())735        }736737        /// Change the owner of the collection.738        /// 739        /// # Permissions740        /// 741        /// * Collection Owner.742        /// 743        /// # Arguments744        /// 745        /// * collection_id.746        /// 747        /// * new_owner.748        #[weight = T::WeightInfo::change_collection_owner()]749        pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {750751            let sender = ensure_signed(origin)?;752            Self::check_owner_permissions(collection_id, sender)?;753            let mut target_collection = <Collection<T>>::get(collection_id);754            target_collection.owner = new_owner;755            <Collection<T>>::insert(collection_id, target_collection);756757            Ok(())758        }759760        /// Adds an admin of the Collection.761        /// 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. 762        /// 763        /// # Permissions764        /// 765        /// * Collection Owner.766        /// * Collection Admin.767        /// 768        /// # Arguments769        /// 770        /// * collection_id: ID of the Collection to add admin for.771        /// 772        /// * new_admin_id: Address of new admin to add.773        #[weight = T::WeightInfo::add_collection_admin()]774        pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {775776            let sender = ensure_signed(origin)?;777            Self::check_owner_or_admin_permissions(collection_id, sender)?;778            let mut admin_arr: Vec<T::AccountId> = Vec::new();779780            if <AdminList<T>>::contains_key(collection_id)781            {782                admin_arr = <AdminList<T>>::get(collection_id);783                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);784            }785786            // Number of collection admins787            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);788789            admin_arr.push(new_admin_id);790            <AdminList<T>>::insert(collection_id, admin_arr);791792            Ok(())793        }794795        /// 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.796        ///797        /// # Permissions798        /// 799        /// * Collection Owner.800        /// * Collection Admin.801        /// 802        /// # Arguments803        /// 804        /// * collection_id: ID of the Collection to remove admin for.805        /// 806        /// * account_id: Address of admin to remove.807        #[weight = T::WeightInfo::remove_collection_admin()]808        pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {809810            let sender = ensure_signed(origin)?;811            Self::check_owner_or_admin_permissions(collection_id, sender)?;812813            if <AdminList<T>>::contains_key(collection_id)814            {815                let mut admin_arr = <AdminList<T>>::get(collection_id);816                admin_arr.retain(|i| *i != account_id);817                <AdminList<T>>::insert(collection_id, admin_arr);818            }819820            Ok(())821        }822823        /// # Permissions824        /// 825        /// * Collection Owner826        /// 827        /// # Arguments828        /// 829        /// * collection_id.830        /// 831        /// * new_sponsor.832        #[weight = T::WeightInfo::set_collection_sponsor()]833        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {834835            let sender = ensure_signed(origin)?;836            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);837838            let mut target_collection = <Collection<T>>::get(collection_id);839            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);840841            target_collection.unconfirmed_sponsor = new_sponsor;842            <Collection<T>>::insert(collection_id, target_collection);843844            Ok(())845        }846847        /// # Permissions848        /// 849        /// * Sponsor.850        /// 851        /// # Arguments852        /// 853        /// * collection_id.854        #[weight = T::WeightInfo::confirm_sponsorship()]855        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {856857            let sender = ensure_signed(origin)?;858            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);859860            let mut target_collection = <Collection<T>>::get(collection_id);861            ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);862863            target_collection.sponsor = target_collection.unconfirmed_sponsor;864            target_collection.unconfirmed_sponsor = T::AccountId::default();865            <Collection<T>>::insert(collection_id, target_collection);866867            Ok(())868        }869870        /// Switch back to pay-per-own-transaction model.871        ///872        /// # Permissions873        ///874        /// * Collection owner.875        /// 876        /// # Arguments877        /// 878        /// * collection_id.879        #[weight = T::WeightInfo::remove_collection_sponsor()]880        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {881882            let sender = ensure_signed(origin)?;883            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);884885            let mut target_collection = <Collection<T>>::get(collection_id);886            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);887888            target_collection.sponsor = T::AccountId::default();889            <Collection<T>>::insert(collection_id, target_collection);890891            Ok(())892        }893894        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.895        /// 896        /// # Permissions897        /// 898        /// * Collection Owner.899        /// * Collection Admin.900        /// * Anyone if901        ///     * White List is enabled, and902        ///     * Address is added to white list, and903        ///     * MintPermission is enabled (see SetMintPermission method)904        /// 905        /// # Arguments906        /// 907        /// * collection_id: ID of the collection.908        /// 909        /// * owner: Address, initial owner of the NFT.910        ///911        /// * data: Token data to store on chain.912        // #[weight =913        // (130_000_000 as Weight)914        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))915        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))916        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]917918        #[weight = T::WeightInfo::create_item(data.len())]919        pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {920921            let sender = ensure_signed(origin)?;922923            Self::collection_exists(collection_id)?;924925            let target_collection = <Collection<T>>::get(collection_id);926927            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;928            Self::validate_create_item_args(&target_collection, &data)?;929            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;930931            Ok(())932        }933934        /// This method creates multiple instances of NFT Collection created with CreateCollection method.935        /// 936        /// # Permissions937        /// 938        /// * Collection Owner.939        /// * Collection Admin.940        /// * Anyone if941        ///     * White List is enabled, and942        ///     * Address is added to white list, and943        ///     * MintPermission is enabled (see SetMintPermission method)944        /// 945        /// # Arguments946        /// 947        /// * collection_id: ID of the collection.948        /// 949        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].950        /// 951        /// * owner: Address, initial owner of the NFT.952        #[weight = T::WeightInfo::create_item(items_data.into_iter()953                               .map(|data| { data.len() })954                               .sum())]955        pub fn create_multiple_items(origin, collection_id: u64, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {956957            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);958            let sender = ensure_signed(origin)?;959960            Self::collection_exists(collection_id)?;961            let target_collection = <Collection<T>>::get(collection_id);962963            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;964965            for data in &items_data {966                Self::validate_create_item_args(&target_collection, data)?;967            }968            for data in &items_data {969                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;970            }971972            Ok(())973        }974975        /// Destroys a concrete instance of NFT.976        /// 977        /// # Permissions978        /// 979        /// * Collection Owner.980        /// * Collection Admin.981        /// * Current NFT Owner.982        /// 983        /// # Arguments984        /// 985        /// * collection_id: ID of the collection.986        /// 987        /// * item_id: ID of NFT to burn.988        #[weight = T::WeightInfo::burn_item()]989        pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {990991            let sender = ensure_signed(origin)?;992            Self::collection_exists(collection_id)?;993994            // Transfer permissions check995            let target_collection = <Collection<T>>::get(collection_id);996            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||997                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),998                Error::<T>::NoPermission);9991000            if target_collection.access == AccessMode::WhiteList {1001                Self::check_white_list(collection_id, &sender)?;1002            }10031004            match target_collection.mode1005            {1006                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1007                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,1008                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1009                _ => ()1010            };10111012            // call event1013            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10141015            Ok(())1016        }10171018        /// Change ownership of the token.1019        /// 1020        /// # Permissions1021        /// 1022        /// * Collection Owner1023        /// * Collection Admin1024        /// * Current NFT owner1025        ///1026        /// # Arguments1027        /// 1028        /// * recipient: Address of token recipient.1029        /// 1030        /// * collection_id.1031        /// 1032        /// * item_id: ID of the item1033        ///     * Non-Fungible Mode: Required.1034        ///     * Fungible Mode: Ignored.1035        ///     * Re-Fungible Mode: Required.1036        /// 1037        /// * value: Amount to transfer.1038        ///     * Non-Fungible Mode: Ignored1039        ///     * Fungible Mode: Must specify transferred amount1040        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1041        #[weight = T::WeightInfo::transfer()]1042        pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {10431044            let sender = ensure_signed(origin)?;10451046            // Transfer permissions check1047            let target_collection = <Collection<T>>::get(collection_id);1048            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1049                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1050                Error::<T>::NoPermission);10511052            if target_collection.access == AccessMode::WhiteList {1053                Self::check_white_list(collection_id, &sender)?;1054                Self::check_white_list(collection_id, &recipient)?;1055            }10561057            match target_collection.mode1058            {1059                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1060                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1061                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1062                _ => ()1063            };10641065            Ok(())1066        }10671068        /// Set, change, or remove approved address to transfer the ownership of the NFT.1069        /// 1070        /// # Permissions1071        /// 1072        /// * Collection Owner1073        /// * Collection Admin1074        /// * Current NFT owner1075        /// 1076        /// # Arguments1077        /// 1078        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1079        /// 1080        /// * collection_id.1081        /// 1082        /// * item_id: ID of the item.1083        #[weight = T::WeightInfo::approve()]1084        pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {10851086            let sender = ensure_signed(origin)?;10871088            // Transfer permissions check1089            let target_collection = <Collection<T>>::get(collection_id);1090            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1091                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1092                Error::<T>::NoPermission);10931094            if target_collection.access == AccessMode::WhiteList {1095                Self::check_white_list(collection_id, &sender)?;1096                Self::check_white_list(collection_id, &approved)?;1097            }10981099            // amount param stub1100            let amount = 100000000;11011102            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1103            if list_exists {11041105                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1106                let item_contains = list.iter().any(|i| i.approved == approved);11071108                if !item_contains {1109                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1110                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1111                }1112            } else {11131114                let mut list = Vec::new();1115                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1116                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1117            }11181119            Ok(())1120        }1121        1122        /// 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.1123        /// 1124        /// # Permissions1125        /// * Collection Owner1126        /// * Collection Admin1127        /// * Current NFT owner1128        /// * Address approved by current NFT owner1129        /// 1130        /// # Arguments1131        /// 1132        /// * from: Address that owns token.1133        /// 1134        /// * recipient: Address of token recipient.1135        /// 1136        /// * collection_id.1137        /// 1138        /// * item_id: ID of the item.1139        /// 1140        /// * value: Amount to transfer.1141        #[weight = T::WeightInfo::transfer_from()]1142        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {11431144            let sender = ensure_signed(origin)?;1145            let mut appoved_transfer = false;11461147            // Check approve1148            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1149                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1150                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1151                if opt_item.is_some()1152                {1153                    appoved_transfer = true;1154                    ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1155                }1156            }11571158            // Transfer permissions check1159            let target_collection = <Collection<T>>::get(collection_id);1160                ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1161                Error::<T>::NoPermission);11621163            if target_collection.access == AccessMode::WhiteList {1164                Self::check_white_list(collection_id, &sender)?;1165                Self::check_white_list(collection_id, &recipient)?;1166            }11671168            // remove approve1169            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1170                .into_iter().filter(|i| i.approved != sender.clone()).collect();1171            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);117211731174            match target_collection.mode1175            {1176                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1177                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1178                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1179                _ => ()1180            };11811182            Ok(())1183        }11841185        ///1186        #[weight = 0]1187        pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11881189            // let no_perm_mes = "You do not have permissions to modify this collection";1190            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1191            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1192            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11931194            // // on_nft_received  call11951196            // Self::transfer(origin, collection_id, item_id, new_owner)?;11971198            Ok(())1199        }12001201        /// Set off-chain data schema.1202        /// 1203        /// # Permissions1204        /// 1205        /// * Collection Owner1206        /// * Collection Admin1207        /// 1208        /// # Arguments1209        /// 1210        /// * collection_id.1211        /// 1212        /// * schema: String representing the offchain data schema.1213        #[weight = T::WeightInfo::set_variable_meta_data()]1214        pub fn set_variable_meta_data (1215            origin,1216            collection_id: u64,1217            item_id: u64,1218            data: Vec<u8>1219        ) -> DispatchResult {1220            let sender = ensure_signed(origin)?;1221            1222            Self::collection_exists(collection_id)?;1223            1224            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12251226            // Modify permissions check1227            let target_collection = <Collection<T>>::get(collection_id);1228            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1229                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1230                Error::<T>::NoPermission);12311232            Self::item_exists(collection_id, item_id, &target_collection.mode)?;12331234            match target_collection.mode1235            {1236                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1237                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1238                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1239                _ => fail!(Error::<T>::UnexpectedCollectionType)1240            };12411242            Ok(())1243        }1244        12451246        /// Set off-chain data schema.1247        /// 1248        /// # Permissions1249        /// 1250        /// * Collection Owner1251        /// * Collection Admin1252        /// 1253        /// # Arguments1254        /// 1255        /// * collection_id.1256        /// 1257        /// * schema: String representing the offchain data schema.1258        #[weight = T::WeightInfo::set_offchain_schema()]1259        pub fn set_offchain_schema(1260            origin,1261            collection_id: u64,1262            schema: Vec<u8>1263        ) -> DispatchResult {1264            let sender = ensure_signed(origin)?;1265            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12661267            let mut target_collection = <Collection<T>>::get(collection_id);1268            target_collection.offchain_schema = schema;1269            <Collection<T>>::insert(collection_id, target_collection);12701271            Ok(())1272        }12731274        /// Set const on-chain data schema.1275        /// 1276        /// # Permissions1277        /// 1278        /// * Collection Owner1279        /// * Collection Admin1280        /// 1281        /// # Arguments1282        /// 1283        /// * collection_id.1284        /// 1285        /// * schema: String representing the const on-chain data schema.1286        #[weight = T::WeightInfo::set_const_on_chain_schema()]1287        pub fn set_const_on_chain_schema (1288            origin,1289            collection_id: u64,1290            schema: Vec<u8>1291        ) -> DispatchResult {1292            let sender = ensure_signed(origin)?;1293            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12941295            let mut target_collection = <Collection<T>>::get(collection_id);1296            target_collection.const_on_chain_schema = schema;1297            <Collection<T>>::insert(collection_id, target_collection);12981299            Ok(())1300        }13011302        /// Set variable on-chain data schema.1303        /// 1304        /// # Permissions1305        /// 1306        /// * Collection Owner1307        /// * Collection Admin1308        /// 1309        /// # Arguments1310        /// 1311        /// * collection_id.1312        /// 1313        /// * schema: String representing the variable on-chain data schema.1314        #[weight = T::WeightInfo::set_const_on_chain_schema()]1315        pub fn set_variable_on_chain_schema (1316            origin,1317            collection_id: u64,1318            schema: Vec<u8>1319        ) -> DispatchResult {1320            let sender = ensure_signed(origin)?;1321            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13221323            let mut target_collection = <Collection<T>>::get(collection_id);1324            target_collection.variable_on_chain_schema = schema;1325            <Collection<T>>::insert(collection_id, target_collection);13261327            Ok(())1328        }13291330        // Sudo permissions function1331        #[weight = 0]1332        pub fn set_chain_limits(1333            origin,1334            limits: ChainLimits1335        ) -> DispatchResult {1336            ensure_root(origin)?;1337            <ChainLimit>::put(limits);1338            Ok(())1339        }13401341        /// Enable smart contract self-sponsoring.1342        /// 1343        /// # Permissions1344        /// 1345        /// * Contract Owner1346        /// 1347        /// # Arguments1348        /// 1349        /// * contract address1350        /// * enable flag1351        /// 1352        #[weight = T::WeightInfo::enable_contract_sponsoring()]1353        pub fn enable_contract_sponsoring(1354            origin,1355            contract_address: T::AccountId,1356            enable: bool1357        ) -> DispatchResult {13581359            let sender = ensure_signed(origin)?;13601361            #[cfg(feature = "runtime-benchmarks")]1362            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13631364            let mut is_owner = false;1365            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1366                let owner = <ContractOwner<T>>::get(&contract_address);1367                is_owner = sender == owner;1368            }1369            ensure!(is_owner, Error::<T>::NoPermission);13701371            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1372            Ok(())1373        }13741375        /// Set the rate limit for contract sponsoring to specified number of blocks.1376        /// 1377        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1378        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1379        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1380        /// from contract endowment if there are at least B blocks between such transactions. 1381        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1382        /// 1383        /// # Permissions1384        /// 1385        /// * Contract Owner1386        /// 1387        /// # Arguments1388        /// 1389        /// -`contract_address`: Address of the contract to sponsor1390        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1391        /// 1392        #[weight = 0]1393        pub fn set_contract_sponsoring_rate_limit(1394            origin,1395            contract_address: T::AccountId,1396            rate_limit: T::BlockNumber1397        ) -> DispatchResult {1398            let sender = ensure_signed(origin)?;1399            let mut is_owner = false;1400            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1401                let owner = <ContractOwner<T>>::get(&contract_address);1402                is_owner = sender == owner;1403            }1404            ensure!(is_owner, Error::<T>::NoPermission);14051406            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1407            Ok(())1408        }14091410        #[weight = 0]1411        pub fn set_collection_limits(1412            origin,1413            collection_id: u64,1414            limits: CollectionLimits,1415        ) -> DispatchResult {1416            let sender = ensure_signed(origin)?;1417            Self::check_owner_permissions(collection_id, sender.clone())?;14181419            let mut target_collection = <Collection<T>>::get(collection_id);1420            target_collection.limits = limits;1421            <Collection<T>>::insert(collection_id, target_collection);14221423            Ok(())1424        } 1425    }1426}14271428impl<T: Trait> Module<T> {14291430    fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {14311432        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {14331434            // check token limit and account token limit1435            let total_items: u64 = ItemListIndex::get(collection_id);1436            let account_items: u32 = <AddressTokens<T>>::get(collection_id, sender.clone()).len() as u32;1437            ensure!(collection.limits.token_limit as u64 > total_items,  Error::<T>::CollectionTokenLimitExceeded);1438            ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1439            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1440            Self::check_white_list(collection_id, owner)?;1441            Self::check_white_list(collection_id, sender)?;1442        }14431444        Ok(())1445    }14461447    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1448        match target_collection.mode1449        {1450            CollectionMode::NFT => {1451                if let CreateItemData::NFT(data) = data {1452                    // check sizes1453                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1454                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1455                } else {1456                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1457                }1458            },1459            CollectionMode::Fungible(_) => {1460                if let CreateItemData::Fungible(_) = data {1461                } else {1462                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1463                }1464            },1465            CollectionMode::ReFungible(_) => {1466                if let CreateItemData::ReFungible(data) = data {14671468                    // check sizes1469                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1470                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1471                } else {1472                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1473                }1474            },1475            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1476        };14771478        Ok(())1479    }14801481    fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1482        match data1483        {1484            CreateItemData::NFT(data) => {1485                let item = NftItemType {1486                    collection: collection_id,1487                    owner,1488                    const_data: data.const_data,1489                    variable_data: data.variable_data1490                };14911492                Self::add_nft_item(item)?;1493            },1494            CreateItemData::Fungible(_) => {1495                let item = FungibleItemType {1496                    collection: collection_id,1497                    owner,1498                    value: (10 as u128).pow(collection.decimal_points)1499                };15001501                Self::add_fungible_item(item)?;1502            },1503            CreateItemData::ReFungible(data) => {1504                let mut owner_list = Vec::new();1505                let value = (10 as u128).pow(collection.decimal_points);1506                owner_list.push(Ownership {owner: owner.clone(), fraction: value});15071508                let item = ReFungibleItemType {1509                    collection: collection_id,1510                    owner: owner_list,1511                    const_data: data.const_data,1512                    variable_data: data.variable_data1513                };15141515                Self::add_refungible_item(item)?;1516            }1517        };15181519        // call event1520        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));15211522        Ok(())1523    }15241525    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1526        let current_index = <ItemListIndex>::get(item.collection)1527            .checked_add(1)1528            .ok_or(Error::<T>::NumOverflow)?;1529        let itemcopy = item.clone();1530        let owner = item.owner.clone();1531        let value = item.value as u64;15321533        Self::add_token_index(item.collection, current_index, owner.clone())?;15341535        <ItemListIndex>::insert(item.collection, current_index);1536        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15371538        // Add current block1539        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1540        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1541        1542        // Update balance1543        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1544            .checked_add(value)1545            .ok_or(Error::<T>::NumOverflow)?;1546        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15471548        Ok(())1549    }15501551    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1552        let current_index = <ItemListIndex>::get(item.collection)1553            .checked_add(1)1554            .ok_or(Error::<T>::NumOverflow)?;1555        let itemcopy = item.clone();15561557        let value = item.owner.first().unwrap().fraction as u64;1558        let owner = item.owner.first().unwrap().owner.clone();15591560        Self::add_token_index(item.collection, current_index, owner.clone())?;15611562        <ItemListIndex>::insert(item.collection, current_index);1563        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);15641565        // Add current block1566        let block_number: T::BlockNumber = 0.into();1567        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);15681569        // Update balance1570        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1571            .checked_add(value)1572            .ok_or(Error::<T>::NumOverflow)?;1573        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);15741575        Ok(())1576    }15771578    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1579        let current_index = <ItemListIndex>::get(item.collection)1580            .checked_add(1)1581            .ok_or(Error::<T>::NumOverflow)?;15821583        let item_owner = item.owner.clone();1584        let collection_id = item.collection.clone();1585        Self::add_token_index(collection_id, current_index, item.owner.clone())?;15861587        <ItemListIndex>::insert(collection_id, current_index);1588        <NftItemList<T>>::insert(collection_id, current_index, item);15891590        // Add current block1591        let block_number: T::BlockNumber = 0.into();1592        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);15931594        // Update balance1595        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1596            .checked_add(1)1597            .ok_or(Error::<T>::NumOverflow)?;1598        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);15991600        Ok(())1601    }16021603    fn burn_refungible_item(1604        collection_id: u64,1605        item_id: u64,1606        owner: T::AccountId,1607    ) -> DispatchResult {1608        ensure!(1609            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1610            Error::<T>::TokenNotFound1611        );1612        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1613        let item = collection1614            .owner1615            .iter()1616            .filter(|&i| i.owner == owner)1617            .next()1618            .unwrap();1619        Self::remove_token_index(collection_id, item_id, owner.clone())?;16201621        // remove approve list1622        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));16231624        // update balance1625        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1626            .checked_sub(item.fraction as u64)1627            .ok_or(Error::<T>::NumOverflow)?;1628        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);16291630        <ReFungibleItemList<T>>::remove(collection_id, item_id);16311632        Ok(())1633    }16341635    fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1636        ensure!(1637            <NftItemList<T>>::contains_key(collection_id, item_id),1638            Error::<T>::TokenNotFound1639        );1640        let item = <NftItemList<T>>::get(collection_id, item_id);1641        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16421643        // remove approve list1644        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16451646        // update balance1647        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1648            .checked_sub(1)1649            .ok_or(Error::<T>::NumOverflow)?;1650        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1651        <NftItemList<T>>::remove(collection_id, item_id);16521653        Ok(())1654    }16551656    fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1657        ensure!(1658            <FungibleItemList<T>>::contains_key(collection_id, item_id),1659            Error::<T>::TokenNotFound1660        );1661        let item = <FungibleItemList<T>>::get(collection_id, item_id);1662        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;16631664        // remove approve list1665        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));16661667        // update balance1668        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1669            .checked_sub(item.value as u64)1670            .ok_or(Error::<T>::NumOverflow)?;1671        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);16721673        <FungibleItemList<T>>::remove(collection_id, item_id);16741675        Ok(())1676    }16771678    fn collection_exists(collection_id: u64) -> DispatchResult {1679        ensure!(1680            <Collection<T>>::contains_key(collection_id),1681            Error::<T>::CollectionNotFound1682        );1683        Ok(())1684    }16851686    fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1687        Self::collection_exists(collection_id)?;16881689        let target_collection = <Collection<T>>::get(collection_id);1690        ensure!(1691            subject == target_collection.owner,1692            Error::<T>::NoPermission1693        );16941695        Ok(())1696    }16971698    fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1699        let target_collection = <Collection<T>>::get(collection_id);1700        let mut result: bool = subject == target_collection.owner;1701        let exists = <AdminList<T>>::contains_key(collection_id);17021703        if !result & exists {1704            if <AdminList<T>>::get(collection_id).contains(&subject) {1705                result = true1706            }1707        }17081709        result1710    }17111712    fn check_owner_or_admin_permissions(1713        collection_id: u64,1714        subject: T::AccountId,1715    ) -> DispatchResult {1716        Self::collection_exists(collection_id)?;1717        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());17181719        ensure!(1720            result,1721            Error::<T>::NoPermission1722        );1723        Ok(())1724    }17251726    fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1727        let target_collection = <Collection<T>>::get(collection_id);17281729        match target_collection.mode {1730            CollectionMode::NFT => {1731                <NftItemList<T>>::get(collection_id, item_id).owner == subject1732            }1733            CollectionMode::Fungible(_) => {1734                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1735            }1736            CollectionMode::ReFungible(_) => {1737                <ReFungibleItemList<T>>::get(collection_id, item_id)1738                    .owner1739                    .iter()1740                    .any(|i| i.owner == subject)1741            }1742            CollectionMode::Invalid => false,1743        }1744    }17451746    fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1747        let mes = Error::<T>::AddresNotInWhiteList;1748        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1749        let wl = <WhiteList<T>>::get(collection_id);1750        ensure!(wl.contains(address), mes);17511752        Ok(())1753    }17541755    fn transfer_fungible(1756        collection_id: u64,1757        item_id: u64,1758        value: u64,1759        owner: T::AccountId,1760        new_owner: T::AccountId,1761    ) -> DispatchResult {1762        ensure!(1763            <FungibleItemList<T>>::contains_key(collection_id, item_id),1764            Error::<T>::TokenNotFound1765        );17661767        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1768        let amount = full_item.value;17691770        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);17711772        // update balance1773        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1774            .checked_sub(value)1775            .ok_or(Error::<T>::NumOverflow)?;1776        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);17771778        let mut new_owner_account_id = 0;1779        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1780        if new_owner_items.len() > 0 {1781            new_owner_account_id = new_owner_items[0];1782        }17831784        let val64 = value.into();17851786        // transfer1787        if amount == val64 && new_owner_account_id == 0 {1788            // change owner1789            // new owner do not have account1790            let mut new_full_item = full_item.clone();1791            new_full_item.owner = new_owner.clone();1792            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17931794            // update balance1795            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1796                .checked_add(value)1797                .ok_or(Error::<T>::NumOverflow)?;1798            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17991800            // update index collection1801            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1802        } else {1803            let mut new_full_item = full_item.clone();1804            new_full_item.value -= val64;18051806            // separate amount1807            if new_owner_account_id > 0 {1808                // new owner has account1809                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1810                item.value += val64;18111812                // update balance1813                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1814                    .checked_add(value)1815                    .ok_or(Error::<T>::NumOverflow)?;1816                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18171818                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1819            } else {1820                // new owner do not have account1821                let item = FungibleItemType {1822                    collection: collection_id,1823                    owner: new_owner.clone(),1824                    value: val64,1825                };18261827                Self::add_fungible_item(item)?;1828            }18291830            if amount == val64 {1831                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;18321833                // remove approve list1834                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1835                <FungibleItemList<T>>::remove(collection_id, item_id);1836            }18371838            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1839        }18401841        Ok(())1842    }18431844    fn transfer_refungible(1845        collection_id: u64,1846        item_id: u64,1847        value: u64,1848        owner: T::AccountId,1849        new_owner: T::AccountId,1850    ) -> DispatchResult {1851        ensure!(1852            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1853            Error::<T>::TokenNotFound1854        );18551856        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1857        let item = full_item1858            .owner1859            .iter()1860            .filter(|i| i.owner == owner)1861            .next()1862            .ok_or(Error::<T>::NumOverflow)?;1863        let amount = item.fraction;18641865        ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);18661867        // update balance1868        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1869            .checked_sub(value)1870            .ok_or(Error::<T>::NumOverflow)?;1871        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18721873        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1874            .checked_add(value)1875            .ok_or(Error::<T>::NumOverflow)?;1876        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18771878        let old_owner = item.owner.clone();1879        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1880        let val64 = value.into();18811882        // transfer1883        if amount == val64 && !new_owner_has_account {1884            // change owner1885            // new owner do not have account1886            let mut new_full_item = full_item.clone();1887            new_full_item1888                .owner1889                .iter_mut()1890                .find(|i| i.owner == owner)1891                .unwrap()1892                .owner = new_owner.clone();1893            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18941895            // update index collection1896            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1897        } else {1898            let mut new_full_item = full_item.clone();1899            new_full_item1900                .owner1901                .iter_mut()1902                .find(|i| i.owner == owner)1903                .unwrap()1904                .fraction -= val64;19051906            // separate amount1907            if new_owner_has_account {1908                // new owner has account1909                new_full_item1910                    .owner1911                    .iter_mut()1912                    .find(|i| i.owner == new_owner)1913                    .unwrap()1914                    .fraction += val64;1915            } else {1916                // new owner do not have account1917                new_full_item.owner.push(Ownership {1918                    owner: new_owner.clone(),1919                    fraction: val64,1920                });1921                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1922            }19231924            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1925        }19261927        Ok(())1928    }19291930    fn transfer_nft(1931        collection_id: u64,1932        item_id: u64,1933        sender: T::AccountId,1934        new_owner: T::AccountId,1935    ) -> DispatchResult {1936        ensure!(1937            <NftItemList<T>>::contains_key(collection_id, item_id),1938            Error::<T>::TokenNotFound1939        );19401941        let mut item = <NftItemList<T>>::get(collection_id, item_id);19421943        ensure!(1944            sender == item.owner,1945            Error::<T>::MustBeTokenOwner1946        );19471948        // update balance1949        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1950            .checked_sub(1)1951            .ok_or(Error::<T>::NumOverflow)?;1952        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);19531954        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1955            .checked_add(1)1956            .ok_or(Error::<T>::NumOverflow)?;1957        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19581959        // change owner1960        let old_owner = item.owner.clone();1961        item.owner = new_owner.clone();1962        <NftItemList<T>>::insert(collection_id, item_id, item);19631964        // update index collection1965        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;19661967        // reset approved list1968        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1969        Ok(())1970    }1971    1972    fn item_exists(1973        collection_id: u64,1974        item_id: u64,1975        mode: &CollectionMode1976    ) -> DispatchResult {1977        match mode {1978            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1979            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1980            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1981            _ => ()1982        };1983        1984        Ok(())1985    }19861987    fn set_re_fungible_variable_data(1988        collection_id: u64,1989        item_id: u64,1990        data: Vec<u8>1991    ) -> DispatchResult {1992        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);19931994        item.variable_data = data;19951996        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);19971998        Ok(())1999    }20002001    fn set_nft_variable_data(2002        collection_id: u64,2003        item_id: u64,2004        data: Vec<u8>2005    ) -> DispatchResult {2006        let mut item = <NftItemList<T>>::get(collection_id, item_id);2007        2008        item.variable_data = data;20092010        <NftItemList<T>>::insert(collection_id, item_id, item);2011        2012        Ok(())2013    }20142015    fn init_collection(item: &CollectionType<T::AccountId>) {2016        // check params2017        assert!(2018            item.decimal_points <= 4,2019            "decimal_points parameter must be lower than 4"2020        );2021        assert!(2022            item.name.len() <= 64,2023            "Collection name can not be longer than 63 char"2024        );2025        assert!(2026            item.name.len() <= 256,2027            "Collection description can not be longer than 255 char"2028        );2029        assert!(2030            item.token_prefix.len() <= 16,2031            "Token prefix can not be longer than 15 char"2032        );20332034        // Generate next collection ID2035        let next_id = CreatedCollectionCount::get()2036            .checked_add(1)2037            .unwrap();20382039        CreatedCollectionCount::put(next_id);2040    }20412042    fn init_nft_token(item: &NftItemType<T::AccountId>) {2043        let current_index = <ItemListIndex>::get(item.collection)2044            .checked_add(1)2045            .unwrap();20462047        let item_owner = item.owner.clone();2048        let collection_id = item.collection.clone();2049        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();20502051        <ItemListIndex>::insert(collection_id, current_index);20522053        // Update balance2054        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())2055            .checked_add(1)2056            .unwrap();2057        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2058    }20592060    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2061        let current_index = <ItemListIndex>::get(item.collection)2062            .checked_add(1)2063            .unwrap();2064        let owner = item.owner.clone();2065        let value = item.value as u64;20662067        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20682069        <ItemListIndex>::insert(item.collection, current_index);20702071        // Update balance2072        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2073            .checked_add(value)2074            .unwrap();2075        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2076    }20772078    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2079        let current_index = <ItemListIndex>::get(item.collection)2080            .checked_add(1)2081            .unwrap();20822083        let value = item.owner.first().unwrap().fraction as u64;2084        let owner = item.owner.first().unwrap().owner.clone();20852086        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();20872088        <ItemListIndex>::insert(item.collection, current_index);20892090        // Update balance2091        let new_balance = <Balance<T>>::get(item.collection, owner.clone())2092            .checked_add(value)2093            .unwrap();2094        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2095    }20962097    fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {20982099        // add to account limit2100        if <AccountItemCount<T>>::contains_key(owner.clone()) {21012102            // bound Owned tokens by a single address2103            let count = <AccountItemCount<T>>::get(owner.clone());2104            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);21052106            <AccountItemCount<T>>::insert(owner.clone(), count2107                .checked_add(1)2108                .ok_or(Error::<T>::NumOverflow)?);2109        }2110        else {2111            <AccountItemCount<T>>::insert(owner.clone(), 1);2112        }21132114        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2115        if list_exists {2116            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2117            let item_contains = list.contains(&item_index.clone());21182119            if !item_contains {2120                list.push(item_index.clone());2121            }21222123            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2124        } else {2125            let mut itm = Vec::new();2126            itm.push(item_index.clone());2127            <AddressTokens<T>>::insert(collection_id, owner, itm);2128            2129        }21302131        Ok(())2132    }21332134    fn remove_token_index(2135        collection_id: u64,2136        item_index: u64,2137        owner: T::AccountId,2138    ) -> DispatchResult {21392140        // update counter2141        <AccountItemCount<T>>::insert(owner.clone(), 2142            <AccountItemCount<T>>::get(owner.clone())2143            .checked_sub(1)2144            .ok_or(Error::<T>::NumOverflow)?);214521462147        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());2148        if list_exists {2149            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());2150            let item_contains = list.contains(&item_index.clone());21512152            if item_contains {2153                list.retain(|&item| item != item_index);2154                <AddressTokens<T>>::insert(collection_id, owner, list);2155            }2156        }21572158        Ok(())2159    }21602161    fn move_token_index(2162        collection_id: u64,2163        item_index: u64,2164        old_owner: T::AccountId,2165        new_owner: T::AccountId,2166    ) -> DispatchResult {2167        Self::remove_token_index(collection_id, item_index, old_owner)?;2168        Self::add_token_index(collection_id, item_index, new_owner)?;21692170        Ok(())2171    }2172}21732174////////////////////////////////////////////////////////////////////////////////////////////////////2175// Economic models2176// #region21772178/// Fee multiplier.2179pub type Multiplier = FixedU128;21802181type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2182    <T as system::Trait>::AccountId,2183>>::Balance;2184type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2185    <T as system::Trait>::AccountId,2186>>::NegativeImbalance;21872188/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2189/// in the queue.2190#[derive(Encode, Decode, Clone, Eq, PartialEq)]2191pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2192    #[codec(compact)] BalanceOf<T>2193);21942195impl<T: Trait + Send + Sync> sp_std::fmt::Debug2196    for ChargeTransactionPayment<T>2197{2198    #[cfg(feature = "std")]2199    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2200        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2201    }2202    #[cfg(not(feature = "std"))]2203    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2204        Ok(())2205    }2206}22072208impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2209where2210    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2211    BalanceOf<T>: Send + Sync + FixedPointOperand,2212{2213    /// utility constructor. Used only in client/factory code.2214    pub fn from(fee: BalanceOf<T>) -> Self {2215        Self(fee)2216    }22172218    pub fn traditional_fee(2219        len: usize,2220        info: &DispatchInfoOf<T::Call>,2221        tip: BalanceOf<T>,2222    ) -> BalanceOf<T>2223    where2224        T::Call: Dispatchable<Info = DispatchInfo>,2225    {2226        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2227    }22282229    fn withdraw_fee(2230        &self,2231        who: &T::AccountId,2232        call: &T::Call,2233        info: &DispatchInfoOf<T::Call>,2234        len: usize,2235    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2236        let tip = self.0;22372238        // Set fee based on call type. Creating collection costs 1 Unique.2239        // All other transactions have traditional fees so far2240        // let fee = match call.is_sub_type() {2241        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2242        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2243        //                                                 // _ => <BalanceOf<T>>::from(100)2244        // };2245        let fee = Self::traditional_fee(len, info, tip);22462247        // Determine who is paying transaction fee based on ecnomic model2248        // Parse call to extract collection ID and access collection sponsor2249        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2250            Some(Call::create_item(collection_id, _owner, _properties)) => {22512252                // check free create limit2253                if <Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)2254                {2255                    <Collection<T>>::get(collection_id).sponsor2256                } else {2257                    T::AccountId::default()2258                }2259            }2260            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2261                2262                let _collection_limits = <Collection<T>>::get(collection_id).limits;2263                let _collection_mode = <Collection<T>>::get(collection_id).mode;22642265                // sponsor timeout2266                let sponsor_transfer = match _collection_mode {2267                    CollectionMode::NFT => {22682269                        // get correct limit2270                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2271                            _collection_limits.sponsor_transfer_timeout2272                        } else {2273                            ChainLimit::get().nft_sponsor_transfer_timeout2274                        };22752276                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2277                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2278                        let limit_time = basket + limit.into();2279                        if block_number >= limit_time {2280                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2281                            true2282                        }2283                        else {2284                            false2285                        }2286                    }2287                    CollectionMode::Fungible(_) => {22882289                        // get correct limit2290                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2291                            _collection_limits.sponsor_transfer_timeout2292                        } else {2293                            ChainLimit::get().fungible_sponsor_transfer_timeout2294                        };22952296                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2297                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2298                        if basket.iter().any(|i| i.address == _new_owner.clone())2299                        {2300                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2301                            let limit_time = item.start_block + limit.into();2302                            if block_number >= limit_time {2303                                basket.retain(|x| x.address == item.address);2304                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2305                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2306                                true2307                            }2308                            else {2309                                false2310                            }2311                        }2312                        else {2313                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2314                            true2315                        }2316                    }2317                    CollectionMode::ReFungible(_) => {23182319                        // get correct limit2320                        let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2321                            _collection_limits.sponsor_transfer_timeout2322                        } else {2323                            ChainLimit::get().refungible_sponsor_transfer_timeout2324                        };23252326                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2327                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2328                        let limit_time = basket + limit.into();2329                        if block_number >= limit_time {2330                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2331                            true2332                        } else {2333                            false2334                        }2335                    }2336                    _ => {2337                        false2338                    },2339                };23402341                if !sponsor_transfer {2342                    T::AccountId::default()2343                } else {2344                    <Collection<T>>::get(collection_id).sponsor2345                }2346            }23472348            _ => T::AccountId::default(),2349        };23502351        // Sponsor smart contracts2352        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {23532354            // On instantiation: set the contract owner2355            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {23562357                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2358                    code_hash,2359                    &data,2360                    &who,2361                );2362                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());23632364                T::AccountId::default()2365            },23662367            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2368            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {23692370                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());23712372                let mut sponsor_transfer = false;2373                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2374                    let last_tx_block = <ContractSponsorBasket<T>>::get(&called_contract);2375                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2376                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2377                    let limit_time = last_tx_block + rate_limit;23782379                    if block_number >= limit_time {2380                        <ContractSponsorBasket<T>>::insert(called_contract.clone(), block_number);2381                        sponsor_transfer = true;2382                    }2383                } else {2384                    sponsor_transfer = false;2385                }2386               2387                2388                let mut sp = T::AccountId::default();2389                if sponsor_transfer {2390                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2391                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2392                            sp = called_contract;2393                        }2394                    }2395                }23962397                sp2398            },23992400            _ => sponsor,2401        };24022403        let mut who_pays_fee: T::AccountId = sponsor.clone();2404        if sponsor == T::AccountId::default() {2405            who_pays_fee = who.clone();2406        }24072408        // Only mess with balances if fee is not zero.2409        if fee.is_zero() {2410            return Ok((fee, None));2411        }24122413        match <T as transaction_payment::Trait>::Currency::withdraw(2414            &who_pays_fee,2415            fee,2416            if tip.is_zero() {2417                WithdrawReason::TransactionPayment.into()2418            } else {2419                WithdrawReason::TransactionPayment | WithdrawReason::Tip2420            },2421            ExistenceRequirement::KeepAlive,2422        ) {2423            Ok(imbalance) => Ok((fee, Some(imbalance))),2424            Err(_) => Err(InvalidTransaction::Payment.into()),2425        }2426    }2427}242824292430impl<T: Trait + Send + Sync> SignedExtension2431    for ChargeTransactionPayment<T>2432where2433    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2434    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2435{2436    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2437    type AccountId = T::AccountId;2438    type Call = T::Call;2439    type AdditionalSigned = ();2440    type Pre = (2441        BalanceOf<T>,2442        Self::AccountId,2443        Option<NegativeImbalanceOf<T>>,2444        BalanceOf<T>,2445    );2446    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2447        Ok(())2448    }24492450    fn validate(2451        &self,2452        _who: &Self::AccountId,2453        _call: &Self::Call,2454        _info: &DispatchInfoOf<Self::Call>,2455        _len: usize,2456    ) -> TransactionValidity {2457        Ok(ValidTransaction::default())2458    }24592460    fn pre_dispatch(2461        self,2462        who: &Self::AccountId,2463        call: &Self::Call,2464        info: &DispatchInfoOf<Self::Call>,2465        len: usize,2466    ) -> Result<Self::Pre, TransactionValidityError> {2467        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2468        Ok((self.0, who.clone(), imbalance, fee))2469    }24702471    fn post_dispatch(2472        pre: Self::Pre,2473        info: &DispatchInfoOf<Self::Call>,2474        post_info: &PostDispatchInfoOf<Self::Call>,2475        len: usize,2476        _result: &DispatchResult,2477    ) -> Result<(), TransactionValidityError> {2478        let (tip, who, imbalance, fee) = pre;2479        if let Some(payed) = imbalance {2480            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2481                len as u32, info, post_info, tip,2482            );2483            let refund = fee.saturating_sub(actual_fee);2484            let actual_payment =2485                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2486                    &who, refund,2487                ) {2488                    Ok(refund_imbalance) => {2489                        // The refund cannot be larger than the up front payed max weight.2490                        // `PostDispatchInfo::calc_unspent` guards against such a case.2491                        match payed.offset(refund_imbalance) {2492                            Ok(actual_payment) => actual_payment,2493                            Err(_) => return Err(InvalidTransaction::Payment.into()),2494                        }2495                    }2496                    // We do not recreate the account using the refund. The up front payment2497                    // is gone in that case.2498                    Err(_) => payed,2499                };2500            let imbalances = actual_payment.split(tip);2501            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2502                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2503            );2504        }2505        Ok(())2506    }2507}25082509// #endregion