git.delta.rocks / unique-network / refs/commits / 3950758e00c3

difftreelog

Merge branch 'develop' into feature/NFTPAR-142

sotmorskiy2020-11-11parents: #f58e687 #6d406a4.patch.diff
in: master
# Conflicts:
#	pallets/nft/src/default_weights.rs
#	pallets/nft/src/lib.rs

6 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3737,6 +3737,7 @@
  "frame-support",
  "frame-system",
  "log",
+ "pallet-contracts",
  "pallet-transaction-payment",
  "parity-scale-codec",
  "serde",
modifiedpallets/nft/Cargo.tomldiffbeforeafterboth
--- a/pallets/nft/Cargo.toml
+++ b/pallets/nft/Cargo.toml
@@ -73,6 +73,34 @@
 branch = 'v2.0.0_release'
 optional = true
 
+[dependencies.pallet-contracts]
+default-features = false
+git = 'https://github.com/usetech-llc/substrate.git'
+package = 'pallet-contracts'
+branch = 'v2.0.0_release'
+version = '2.0.0'
+
+[dependencies.pallet-balances]
+default-features = false
+git = 'https://github.com/usetech-llc/substrate.git'
+package = 'pallet-balances'
+branch = 'v2.0.0_release'
+version = '2.0.0'
+
+[dependencies.pallet-timestamp]
+default-features = false
+git = 'https://github.com/usetech-llc/substrate.git'
+package = 'pallet-timestamp'
+branch = 'v2.0.0_release'
+version = '2.0.0'
+
+[dependencies.pallet-randomness-collective-flip]
+default-features = false
+git = 'https://github.com/usetech-llc/substrate.git'
+package = 'pallet-randomness-collective-flip'
+branch = 'v2.0.0_release'
+version = '2.0.0'
+
 [features]
 default = ['std']
 std = [
@@ -80,6 +108,9 @@
     "serde/std",
     'frame-support/std',
     'frame-system/std',
+    'pallet-balances/std',
+    'pallet-timestamp/std',
+    'pallet-randomness-collective-flip/std',
     'sp-std/std',
     'sp-runtime/std',
     'frame-benchmarking/std',
modifiedpallets/nft/src/default_weights.rsdiffbeforeafterboth
--- a/pallets/nft/src/default_weights.rs
+++ b/pallets/nft/src/default_weights.rs
@@ -107,4 +107,9 @@
             .saturating_add(DbWeight::get().reads(2 as Weight))
             .saturating_add(DbWeight::get().writes(1 as Weight))
     }
+    // fn enable_contract_sponsoring() -> Weight {
+    //     (0 as Weight)
+    //         .saturating_add(DbWeight::get().reads(1 as Weight))
+    //         .saturating_add(DbWeight::get().writes(1 as Weight))
+    // }
 }
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,12    dispatch::DispatchResult,13    ensure, parameter_types, fail,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};25// use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight};2627use frame_system::{self as system, ensure_signed, ensure_root};28use sp_runtime::sp_std::prelude::Vec;29use sp_runtime::{30    traits::{31        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SaturatedConversion, Saturating,32        SignedExtension, Zero,33    },34    transaction_validity::{35        InvalidTransaction, TransactionPriority, TransactionValidity, TransactionValidityError,36        ValidTransaction,37    },38    FixedPointOperand, FixedU128,39};4041#[cfg(test)]42mod mock;4344#[cfg(test)]45mod tests;4647mod default_weights;4849// Structs50// #region5152#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]53#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]54pub enum CollectionMode {55    Invalid,56    NFT,57    // decimal points58    Fungible(u32),59    // decimal points60    ReFungible(u32),61}6263impl Into<u8> for CollectionMode {64    fn into(self) -> u8 {65        match self {66            CollectionMode::Invalid => 0,67            CollectionMode::NFT => 1,68            CollectionMode::Fungible(_) => 2,69            CollectionMode::ReFungible(_) => 3,70        }71    }72}7374#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]75#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]76pub enum AccessMode {77    Normal,78    WhiteList,79}80impl Default for AccessMode {81    fn default() -> Self {82        Self::Normal83    }84}8586impl Default for CollectionMode {87    fn default() -> Self {88        Self::Invalid89    }90}9192#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]93#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]94pub struct Ownership<AccountId> {95    pub owner: AccountId,96    pub fraction: u128,97}9899#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]100#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]101pub struct CollectionType<AccountId> {102    pub owner: AccountId,103    pub mode: CollectionMode,104    pub access: AccessMode,105    pub decimal_points: u32,106    pub name: Vec<u16>,        // 64 include null escape char107    pub description: Vec<u16>, // 256 include null escape char108    pub token_prefix: Vec<u8>, // 16 include null escape char109    pub mint_mode: bool,110    pub offchain_schema: Vec<u8>,111    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender112    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship113    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 CollectionAdminsType<AccountId> {120    pub admin: AccountId,121    pub collection_id: u64,122}123124#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]125#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]126pub struct NftItemType<AccountId> {127    pub collection: u64,128    pub owner: AccountId,129    pub const_data: Vec<u8>,130    pub variable_data: Vec<u8>,131}132133#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]134#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]135pub struct FungibleItemType<AccountId> {136    pub collection: u64,137    pub owner: AccountId,138    pub value: u128,139}140141#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]142#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]143pub struct ReFungibleItemType<AccountId> {144    pub collection: u64,145    pub owner: Vec<Ownership<AccountId>>,146    pub const_data: Vec<u8>,147    pub variable_data: Vec<u8>,148}149150#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]151#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]152pub struct ApprovePermissions<AccountId> {153    pub approved: AccountId,154    pub amount: u64,155}156157#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]158#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]159pub struct VestingItem<AccountId, Moment> {160    pub sender: AccountId,161    pub recipient: AccountId,162    pub collection_id: u64,163    pub item_id: u64,164    pub amount: u64,165    pub vesting_date: Moment,166}167168#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]169#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170pub struct BasketItem<AccountId, BlockNumber> {171    pub address: AccountId,172    pub start_block: BlockNumber,173}174175#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]176#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]177pub struct ChainLimits {178    pub collection_numbers_limit: u64,179    pub account_token_ownership_limit: u64,180    pub collections_admins_limit: u64,181    pub custom_data_limit: u32,182183    // Timeouts for item types in passed blocks184    pub nft_sponsor_transfer_timeout: u32,185    pub fungible_sponsor_transfer_timeout: u32,186    pub refungible_sponsor_transfer_timeout: u32,187}188189pub trait WeightInfo {190	fn create_collection() -> Weight;191	fn destroy_collection() -> Weight;192	fn add_to_white_list() -> Weight;193	fn remove_from_white_list() -> Weight;194    fn set_public_access_mode() -> Weight;195    fn set_mint_permission() -> Weight;196    fn change_collection_owner() -> Weight;197    fn add_collection_admin() -> Weight;198    fn remove_collection_admin() -> Weight;199    fn set_collection_sponsor() -> Weight;200    fn confirm_sponsorship() -> Weight;201    fn remove_collection_sponsor() -> Weight;202    fn create_item(s: usize) -> Weight;203    fn burn_item() -> Weight;204    fn transfer() -> Weight;205    fn approve() -> Weight;206    fn transfer_from() -> Weight;207    fn set_offchain_schema() -> Weight;208    fn set_const_on_chain_schema() -> Weight;209    fn set_variable_on_chain_schema() -> Weight;210    fn set_variable_meta_data() -> Weight;211}212213#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]214#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]215pub struct CreateNftData {216    pub const_data: Vec<u8>,217    pub variable_data: Vec<u8>,218}219220#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]221#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]222pub struct CreateFungibleData {223}224225#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]226#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]227pub struct CreateReFungibleData {228    pub const_data: Vec<u8>,229    pub variable_data: Vec<u8>,230}231232#[derive(Encode, Decode, Debug, Clone, PartialEq)]233#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]234pub enum CreateItemData {235    NFT(CreateNftData),236    Fungible(CreateFungibleData),237    ReFungible(CreateReFungibleData)238}239240impl CreateItemData {241    pub fn len(&self) -> usize {242        let len = match self {243            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),244            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),245            _ => 0246        };247        248        return len;249    }250}251252pub trait Trait: system::Trait + Sized  {253    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;254255    /// Weight information for extrinsics in this pallet.256	type WeightInfo: WeightInfo;257}258259#[cfg(feature = "runtime-benchmarks")]260mod benchmarking;261262// #endregion263264decl_storage! {265    trait Store for Module<T: Trait> as Nft {266267        // Private members268        NextCollectionID: u64;269        CreatedCollectionCount: u64;270        ChainVersion: u64;271        ItemListIndex: map hasher(blake2_128_concat) u64 => u64;272273        // Chain limits struct274        pub ChainLimit get(fn chain_limit) config(): ChainLimits;275276        // Bound counters277        CollectionCount: u64;278        pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;279280        // Basic collections281        pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;282        pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;283        pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;284285        /// Balance owner per collection map286        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;287288        /// second parameter: item id + owner account id289        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;290291        /// Item collections292        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;293        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;294        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;295296        /// Index list297        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;298299        /// Tokens transfer baskets300        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;301        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>>;302        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;303304        // Sponsorship305        pub ContractSponsor get(fn contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;306        pub UnconfirmedContractSponsor get(fn unconfirmed_contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;307    }308    add_extra_genesis {309        build(|config: &GenesisConfig<T>| {310            // Modification of storage311            for (_num, _c) in &config.collection {312                <Module<T>>::init_collection(_c);313            }314315            for (_num, _q, _i) in &config.nft_item_id {316                <Module<T>>::init_nft_token(_i);317            }318319            for (_num, _q, _i) in &config.fungible_item_id {320                <Module<T>>::init_fungible_token(_i);321            }322323            for (_num, _q, _i) in &config.refungible_item_id {324                <Module<T>>::init_refungible_token(_i);325            }326        })327    }328}329330decl_event!(331    pub enum Event<T>332    where333        AccountId = <T as system::Trait>::AccountId,334    {335        /// New collection was created336        /// 337        /// # Arguments338        /// 339        /// * collection_id: Globally unique identifier of newly created collection.340        /// 341        /// * mode: [CollectionMode] converted into u8.342        /// 343        /// * account_id: Collection owner.344        Created(u64, u8, AccountId),345346        /// New item was created.347        /// 348        /// # Arguments349        /// 350        /// * collection_id: Id of the collection where item was created.351        /// 352        /// * item_id: Id of an item. Unique within the collection.353        ItemCreated(u64, u64),354355        /// Collection item was burned.356        /// 357        /// # Arguments358        /// 359        /// collection_id.360        /// 361        /// item_id: Identifier of burned NFT.362        ItemDestroyed(u64, u64),363    }364);365366decl_module! {367    pub struct Module<T: Trait> for enum Call where origin: T::Origin {368369        fn deposit_event() = default;370371        fn on_initialize(now: T::BlockNumber) -> Weight {372373            if ChainVersion::get() < 2374            {375                let value = NextCollectionID::get();376                CreatedCollectionCount::put(value);377                ChainVersion::put(2);378            }379380            0381        }382383        /// 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.384        /// 385        /// # Permissions386        /// 387        /// * Anyone.388        /// 389        /// # Arguments390        /// 391        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.392        /// 393        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.394        /// 395        /// * token_prefix: UTF-8 string with token prefix.396        /// 397        /// * mode: [CollectionMode] collection type and type dependent data.398        // returns collection ID399        #[weight = T::WeightInfo::create_collection()]400        pub fn create_collection(origin,401                                 collection_name: Vec<u16>,402                                 collection_description: Vec<u16>,403                                 token_prefix: Vec<u8>,404                                 mode: CollectionMode) -> DispatchResult {405406            // Anyone can create a collection407            let who = ensure_signed(origin)?;408409            let decimal_points = match mode {410                CollectionMode::Fungible(points) => points,411                CollectionMode::ReFungible(points) => points,412                _ => 0413            };414415            // bound Total number of collections416            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");417418            // check params419            ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");420421            let mut name = collection_name.to_vec();422            name.push(0);423            ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");424425            let mut description = collection_description.to_vec();426            description.push(0);427            ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");428429            let mut prefix = token_prefix.to_vec();430            prefix.push(0);431            ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");432433            // Generate next collection ID434            let next_id = CreatedCollectionCount::get()435                .checked_add(1)436                .expect("collection id error");437438            // bound counter439            let total = CollectionCount::get()440                .checked_add(1)441                .expect("collection counter error");442443            CreatedCollectionCount::put(next_id);444            CollectionCount::put(total);445446            // Create new collection447            let new_collection = CollectionType {448                owner: who.clone(),449                name: name,450                mode: mode.clone(),451                mint_mode: false,452                access: AccessMode::Normal,453                description: description,454                decimal_points: decimal_points,455                token_prefix: prefix,456                offchain_schema: Vec::new(),457                sponsor: T::AccountId::default(),458                unconfirmed_sponsor: T::AccountId::default(),459                variable_on_chain_schema: Vec::new(),460                const_on_chain_schema: Vec::new(),461            };462463            // Add new collection to map464            <Collection<T>>::insert(next_id, new_collection);465466            // call event467            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));468469            Ok(())470        }471472        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.473        ///     474        /// # Permissions475        /// 476        /// * Collection Owner.477        /// 478        /// # Arguments479        /// 480        /// * collection_id: collection to destroy.481        #[weight = T::WeightInfo::destroy_collection()]482        pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {483484            let sender = ensure_signed(origin)?;485            Self::check_owner_permissions(collection_id, sender)?;486487            <AddressTokens<T>>::remove_prefix(collection_id);488            <ApprovedList<T>>::remove_prefix(collection_id);489            <Balance<T>>::remove_prefix(collection_id);490            <ItemListIndex>::remove(collection_id);491            <AdminList<T>>::remove(collection_id);492            <Collection<T>>::remove(collection_id);493            <WhiteList<T>>::remove(collection_id);494495            <NftItemList<T>>::remove_prefix(collection_id);496            <FungibleItemList<T>>::remove_prefix(collection_id);497            <ReFungibleItemList<T>>::remove_prefix(collection_id);498499            <NftTransferBasket<T>>::remove_prefix(collection_id);500            <FungibleTransferBasket<T>>::remove_prefix(collection_id);501            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);502503            if CollectionCount::get() > 0504            {505                // bound couter506                let total = CollectionCount::get()507                    .checked_sub(1)508                    .expect("collection counter error");509510                CollectionCount::put(total);511            }512513            Ok(())514        }515516        /// Add an address to white list.517        /// 518        /// # Permissions519        /// 520        /// * Collection Owner521        /// * Collection Admin522        /// 523        /// # Arguments524        /// 525        /// * collection_id.526        /// 527        /// * address.528        #[weight = T::WeightInfo::add_to_white_list()]529        pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{530531            let sender = ensure_signed(origin)?;532            Self::check_owner_or_admin_permissions(collection_id, sender)?;533534            let mut white_list_collection: Vec<T::AccountId>;535            if <WhiteList<T>>::contains_key(collection_id) {536                white_list_collection = <WhiteList<T>>::get(collection_id);537                if !white_list_collection.contains(&address.clone())538                {539                    white_list_collection.push(address.clone());540                }541            }542            else {543                white_list_collection = Vec::new();544                white_list_collection.push(address.clone());545            }546547            <WhiteList<T>>::insert(collection_id, white_list_collection);548            Ok(())549        }550551        /// Remove an address from white list.552        /// 553        /// # Permissions554        /// 555        /// * Collection Owner556        /// * Collection Admin557        /// 558        /// # Arguments559        /// 560        /// * collection_id.561        /// 562        /// * address.563        #[weight = T::WeightInfo::remove_from_white_list()]564        pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{565566            let sender = ensure_signed(origin)?;567            Self::check_owner_or_admin_permissions(collection_id, sender)?;568569            if <WhiteList<T>>::contains_key(collection_id) {570                let mut white_list_collection = <WhiteList<T>>::get(collection_id);571                if white_list_collection.contains(&address.clone())572                {573                    white_list_collection.retain(|i| *i != address.clone());574                    <WhiteList<T>>::insert(collection_id, white_list_collection);575                }576            }577578            Ok(())579        }580581        /// Toggle between normal and white list access for the methods with access for `Anyone`.582        /// 583        /// # Permissions584        /// 585        /// * Collection Owner.586        /// 587        /// # Arguments588        /// 589        /// * collection_id.590        /// 591        /// * mode: [AccessMode]592        #[weight = T::WeightInfo::set_public_access_mode()]593        pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult594        {595            let sender = ensure_signed(origin)?;596597            Self::check_owner_permissions(collection_id, sender)?;598            let mut target_collection = <Collection<T>>::get(collection_id);599            target_collection.access = mode;600            <Collection<T>>::insert(collection_id, target_collection);601602            Ok(())603        }604605        /// Allows Anyone to create tokens if:606        /// * White List is enabled, and607        /// * Address is added to white list, and608        /// * This method was called with True parameter609        /// 610        /// # Permissions611        /// * Collection Owner612        ///613        /// # Arguments614        /// 615        /// * collection_id.616        /// 617        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.618        #[weight = T::WeightInfo::set_mint_permission()]619        pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult620        {621            let sender = ensure_signed(origin)?;622623            Self::check_owner_permissions(collection_id, sender)?;624            let mut target_collection = <Collection<T>>::get(collection_id);625            target_collection.mint_mode = mint_permission;626            <Collection<T>>::insert(collection_id, target_collection);627628            Ok(())629        }630631        /// Change the owner of the collection.632        /// 633        /// # Permissions634        /// 635        /// * Collection Owner.636        /// 637        /// # Arguments638        /// 639        /// * collection_id.640        /// 641        /// * new_owner.642        #[weight = T::WeightInfo::change_collection_owner()]643        pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {644645            let sender = ensure_signed(origin)?;646            Self::check_owner_permissions(collection_id, sender)?;647            let mut target_collection = <Collection<T>>::get(collection_id);648            target_collection.owner = new_owner;649            <Collection<T>>::insert(collection_id, target_collection);650651            Ok(())652        }653654        /// Adds an admin of the Collection.655        /// 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. 656        /// 657        /// # Permissions658        /// 659        /// * Collection Owner.660        /// * Collection Admin.661        /// 662        /// # Arguments663        /// 664        /// * collection_id: ID of the Collection to add admin for.665        /// 666        /// * new_admin_id: Address of new admin to add.667        #[weight = T::WeightInfo::add_collection_admin()]668        pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {669670            let sender = ensure_signed(origin)?;671            Self::check_owner_or_admin_permissions(collection_id, sender)?;672            let mut admin_arr: Vec<T::AccountId> = Vec::new();673674            if <AdminList<T>>::contains_key(collection_id)675            {676                admin_arr = <AdminList<T>>::get(collection_id);677                ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");678            }679680            // Number of collection admins681            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");682683            admin_arr.push(new_admin_id);684            <AdminList<T>>::insert(collection_id, admin_arr);685686            Ok(())687        }688689        /// 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.690        ///691        /// # Permissions692        /// 693        /// * Collection Owner.694        /// * Collection Admin.695        /// 696        /// # Arguments697        /// 698        /// * collection_id: ID of the Collection to remove admin for.699        /// 700        /// * account_id: Address of admin to remove.701        #[weight = T::WeightInfo::remove_collection_admin()]702        pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {703704            let sender = ensure_signed(origin)?;705            Self::check_owner_or_admin_permissions(collection_id, sender)?;706707            if <AdminList<T>>::contains_key(collection_id)708            {709                let mut admin_arr = <AdminList<T>>::get(collection_id);710                admin_arr.retain(|i| *i != account_id);711                <AdminList<T>>::insert(collection_id, admin_arr);712            }713714            Ok(())715        }716717        /// # Permissions718        /// 719        /// * Collection Owner720        /// 721        /// # Arguments722        /// 723        /// * collection_id.724        /// 725        /// * new_sponsor.726        #[weight = T::WeightInfo::set_collection_sponsor()]727        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {728729            let sender = ensure_signed(origin)?;730            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");731732            let mut target_collection = <Collection<T>>::get(collection_id);733            ensure!(sender == target_collection.owner, "You do not own this collection");734735            target_collection.unconfirmed_sponsor = new_sponsor;736            <Collection<T>>::insert(collection_id, target_collection);737738            Ok(())739        }740741        /// # Permissions742        /// 743        /// * Sponsor.744        /// 745        /// # Arguments746        /// 747        /// * collection_id.748        #[weight = T::WeightInfo::confirm_sponsorship()]749        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {750751            let sender = ensure_signed(origin)?;752            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");753754            let mut target_collection = <Collection<T>>::get(collection_id);755            ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");756757            target_collection.sponsor = target_collection.unconfirmed_sponsor;758            target_collection.unconfirmed_sponsor = T::AccountId::default();759            <Collection<T>>::insert(collection_id, target_collection);760761            Ok(())762        }763764        /// Switch back to pay-per-own-transaction model.765        ///766        /// # Permissions767        ///768        /// * Collection owner.769        /// 770        /// # Arguments771        /// 772        /// * collection_id.773        #[weight = T::WeightInfo::remove_collection_sponsor()]774        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {775776            let sender = ensure_signed(origin)?;777            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");778779            let mut target_collection = <Collection<T>>::get(collection_id);780            ensure!(sender == target_collection.owner, "You do not own this collection");781782            target_collection.sponsor = T::AccountId::default();783            <Collection<T>>::insert(collection_id, target_collection);784785            Ok(())786        }787788        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.789        /// 790        /// # Permissions791        /// 792        /// * Collection Owner.793        /// * Collection Admin.794        /// * Anyone if795        ///     * White List is enabled, and796        ///     * Address is added to white list, and797        ///     * MintPermission is enabled (see SetMintPermission method)798        /// 799        /// # Arguments800        /// 801        /// * collection_id: ID of the collection.802        /// 803        /// * owner: Address, initial owner of the NFT.804        ///805        /// * data: Token data to store on chain.806        // #[weight =807        // (130_000_000 as Weight)808        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))809        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))810        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]811812        #[weight = T::WeightInfo::create_item(data.len())]813        pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {814815            let sender = ensure_signed(origin)?;816            Self::collection_exists(collection_id)?;817            let target_collection = <Collection<T>>::get(collection_id);818819            if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {820                ensure!(target_collection.mint_mode == true, "Public minting is not allowed for this collection.");821                Self::check_white_list(collection_id, &owner)?;822                Self::check_white_list(collection_id, &sender)?;823            }824825            match target_collection.mode826            {827                CollectionMode::NFT => {828                    if let CreateItemData::NFT(data) = data {829                        // check sizes830                        ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");831                        ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");832    833                        // Create nft item834                        let item = NftItemType {835                            collection: collection_id,836                            owner: owner,837                            const_data: data.const_data.clone(),838                            variable_data: data.variable_data.clone() 839                        };840    841                        Self::add_nft_item(item)?;842                    843                    } else {844                        fail!("Not NFT item data used to mint in NFT collection.");845                    }846                },847                CollectionMode::Fungible(_) => {848                    if let CreateItemData::Fungible(_) = data {849    850                        let item = FungibleItemType {851                            collection: collection_id,852                            owner: owner,853                            value: (10 as u128).pow(target_collection.decimal_points)854                        };855    856                        Self::add_fungible_item(item)?;857                    } else {858                        fail!("Not Fungible item data used to mint in Fungible collection.");859                    }860                },861                CollectionMode::ReFungible(_) => {862                    if let CreateItemData::ReFungible(data) = data {863    864                        // check sizes865                        ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");866                        ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");867    868                        let mut owner_list = Vec::new();869                        let value = (10 as u128).pow(target_collection.decimal_points);870                        owner_list.push(Ownership {owner: owner.clone(), fraction: value});871    872                        let item = ReFungibleItemType {873                            collection: collection_id,874                            owner: owner_list,875                            const_data: data.const_data.clone(),876                            variable_data: data.variable_data.clone() 877                        };878    879                        Self::add_refungible_item(item)?;880                    } else {881                        fail!("Not Re Fungible item data used to mint in Re Fungible collection.");882                    }883                },884                _ => { ensure!(1 == 0,"Unexpected collection type."); }885            };886887            // call event888            Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));889890            Ok(())891        }892893        /// Destroys a concrete instance of NFT.894        /// 895        /// # Permissions896        /// 897        /// * Collection Owner.898        /// * Collection Admin.899        /// * Current NFT Owner.900        /// 901        /// # Arguments902        /// 903        /// * collection_id: ID of the collection.904        /// 905        /// * item_id: ID of NFT to burn.906        #[weight = T::WeightInfo::burn_item()]907        pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {908909            let sender = ensure_signed(origin)?;910            Self::collection_exists(collection_id)?;911912            // Transfer permissions check913            let target_collection = <Collection<T>>::get(collection_id);914            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||915                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),916                "Only item owner, collection owner and admins can modify item");917918            if target_collection.access == AccessMode::WhiteList {919                Self::check_white_list(collection_id, &sender)?;920            }921922            match target_collection.mode923            {924                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,925                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,926                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,927                _ => ()928            };929930            // call event931            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));932933            Ok(())934        }935936        /// Change ownership of the token.937        /// 938        /// # Permissions939        /// 940        /// * Collection Owner941        /// * Collection Admin942        /// * Current NFT owner943        ///944        /// # Arguments945        /// 946        /// * recipient: Address of token recipient.947        /// 948        /// * collection_id.949        /// 950        /// * item_id: ID of the item951        ///     * Non-Fungible Mode: Required.952        ///     * Fungible Mode: Ignored.953        ///     * Re-Fungible Mode: Required.954        /// 955        /// * value: Amount to transfer.956        ///     * Non-Fungible Mode: Ignored957        ///     * Fungible Mode: Must specify transferred amount958        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)959        #[weight = T::WeightInfo::transfer()]960        pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {961962            let sender = ensure_signed(origin)?;963964            // Transfer permissions check965            let target_collection = <Collection<T>>::get(collection_id);966            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||967                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),968                "Only item owner, collection owner and admins can modify item");969970            if target_collection.access == AccessMode::WhiteList {971                Self::check_white_list(collection_id, &sender)?;972                Self::check_white_list(collection_id, &recipient)?;973            }974975            match target_collection.mode976            {977                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,978                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,979                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,980                _ => ()981            };982983            Ok(())984        }985986        /// Set, change, or remove approved address to transfer the ownership of the NFT.987        /// 988        /// # Permissions989        /// 990        /// * Collection Owner991        /// * Collection Admin992        /// * Current NFT owner993        /// 994        /// # Arguments995        /// 996        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).997        /// 998        /// * collection_id.999        /// 1000        /// * item_id: ID of the item.1001        #[weight = T::WeightInfo::approve()]1002        pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {10031004            let sender = ensure_signed(origin)?;10051006            // Transfer permissions check1007            let target_collection = <Collection<T>>::get(collection_id);1008            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1009                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1010                "Only item owner, collection owner and admins can approve");10111012            if target_collection.access == AccessMode::WhiteList {1013                Self::check_white_list(collection_id, &sender)?;1014                Self::check_white_list(collection_id, &approved)?;1015            }10161017            // amount param stub1018            let amount = 100000000;10191020            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1021            if list_exists {10221023                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1024                let item_contains = list.iter().any(|i| i.approved == approved);10251026                if !item_contains {1027                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1028                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1029                }1030            } else {10311032                let mut list = Vec::new();1033                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1034                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1035            }10361037            Ok(())1038        }1039        1040        /// 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.1041        /// 1042        /// # Permissions1043        /// * Collection Owner1044        /// * Collection Admin1045        /// * Current NFT owner1046        /// * Address approved by current NFT owner1047        /// 1048        /// # Arguments1049        /// 1050        /// * from: Address that owns token.1051        /// 1052        /// * recipient: Address of token recipient.1053        /// 1054        /// * collection_id.1055        /// 1056        /// * item_id: ID of the item.1057        /// 1058        /// * value: Amount to transfer.1059        #[weight = T::WeightInfo::transfer_from()]1060        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10611062            let sender = ensure_signed(origin)?;1063            let mut appoved_transfer = false;10641065            // Check approve1066            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1067                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1068                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1069                if opt_item.is_some()1070                {1071                    appoved_transfer = true;1072                    ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1073                }1074            }10751076            // Transfer permissions check1077            let target_collection = <Collection<T>>::get(collection_id);1078            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1079                "Only item owner, collection owner and admins can modify items");10801081            if target_collection.access == AccessMode::WhiteList {1082                Self::check_white_list(collection_id, &sender)?;1083                Self::check_white_list(collection_id, &recipient)?;1084            }10851086            // remove approve1087            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1088                .into_iter().filter(|i| i.approved != sender.clone()).collect();1089            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);109010911092            match target_collection.mode1093            {1094                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1095                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1096                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1097                _ => ()1098            };10991100            Ok(())1101        }11021103        ///1104        #[weight = 0]1105        pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11061107            // let no_perm_mes = "You do not have permissions to modify this collection";1108            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1109            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1110            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11111112            // // on_nft_received  call11131114            // Self::transfer(origin, collection_id, item_id, new_owner)?;11151116            Ok(())1117        }1118        1119        /// Set off-chain data schema.1120        /// 1121        /// # Permissions1122        /// 1123        /// * Collection Owner1124        /// * Collection Admin1125        /// 1126        /// # Arguments1127        /// 1128        /// * collection_id.1129        /// 1130        /// * schema: String representing the offchain data schema.1131        #[weight = T::WeightInfo::set_variable_meta_data()]1132        pub fn set_variable_meta_data (1133            origin,1134            collection_id: u64,1135            item_id: u64,1136            data: Vec<u8>1137        ) -> DispatchResult {1138            let sender = ensure_signed(origin)?;1139            1140            Self::collection_exists(collection_id)?;11411142            // Modify permissions check1143            let target_collection = <Collection<T>>::get(collection_id);1144            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1145                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1146                "Only item owner, collection owner and admins can modify item");11471148            Self::item_exists(collection_id, item_id, &target_collection.mode)?;11491150            match target_collection.mode1151            {1152                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1153                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1154                _ => ()1155            };11561157            Ok(())1158        }1159        11601161        /// Set off-chain data schema.1162        /// 1163        /// # Permissions1164        /// 1165        /// * Collection Owner1166        /// * Collection Admin1167        /// 1168        /// # Arguments1169        /// 1170        /// * collection_id.1171        /// 1172        /// * schema: String representing the offchain data schema.1173        #[weight = T::WeightInfo::set_offchain_schema()]1174        pub fn set_offchain_schema(1175            origin,1176            collection_id: u64,1177            schema: Vec<u8>1178        ) -> DispatchResult {1179            let sender = ensure_signed(origin)?;1180            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;11811182            let mut target_collection = <Collection<T>>::get(collection_id);1183            target_collection.offchain_schema = schema;1184            <Collection<T>>::insert(collection_id, target_collection);11851186            Ok(())1187        }11881189        /// Set const on-chain data schema.1190        /// 1191        /// # Permissions1192        /// 1193        /// * Collection Owner1194        /// * Collection Admin1195        /// 1196        /// # Arguments1197        /// 1198        /// * collection_id.1199        /// 1200        /// * schema: String representing the const on-chain data schema.1201        #[weight = T::WeightInfo::set_const_on_chain_schema()]1202        pub fn set_const_on_chain_schema (1203            origin,1204            collection_id: u64,1205            schema: Vec<u8>1206        ) -> DispatchResult {1207            let sender = ensure_signed(origin)?;1208            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12091210            let mut target_collection = <Collection<T>>::get(collection_id);1211            target_collection.const_on_chain_schema = schema;1212            <Collection<T>>::insert(collection_id, target_collection);12131214            Ok(())1215        }12161217        /// Set variable on-chain data schema.1218        /// 1219        /// # Permissions1220        /// 1221        /// * Collection Owner1222        /// * Collection Admin1223        /// 1224        /// # Arguments1225        /// 1226        /// * collection_id.1227        /// 1228        /// * schema: String representing the variable on-chain data schema.1229        #[weight = T::WeightInfo::set_const_on_chain_schema()]1230        pub fn set_variable_on_chain_schema (1231            origin,1232            collection_id: u64,1233            schema: Vec<u8>1234        ) -> DispatchResult {1235            let sender = ensure_signed(origin)?;1236            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12371238            let mut target_collection = <Collection<T>>::get(collection_id);1239            target_collection.variable_on_chain_schema = schema;1240            <Collection<T>>::insert(collection_id, target_collection);12411242            Ok(())1243        }12441245        // Sudo permissions function1246        #[weight = 0]1247        pub fn set_chain_limits(1248            origin,1249            limits: ChainLimits1250        ) -> DispatchResult {1251            ensure_root(origin)?;1252            <ChainLimit>::put(limits);1253            Ok(())1254        }        1255    }1256}12571258impl<T: Trait> Module<T> {1259    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1260        let current_index = <ItemListIndex>::get(item.collection)1261            .checked_add(1)1262            .expect("Item list index id error");1263        let itemcopy = item.clone();1264        let owner = item.owner.clone();1265        let value = item.value as u64;12661267        Self::add_token_index(item.collection, current_index, owner.clone())?;12681269        <ItemListIndex>::insert(item.collection, current_index);1270        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);12711272        // Add current block1273        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1274        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1275        1276        // Update balance1277        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1278            .checked_add(value)1279            .unwrap();1280        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);12811282        Ok(())1283    }12841285    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1286        let current_index = <ItemListIndex>::get(item.collection)1287            .checked_add(1)1288            .expect("Item list index id error");1289        let itemcopy = item.clone();12901291        let value = item.owner.first().unwrap().fraction as u64;1292        let owner = item.owner.first().unwrap().owner.clone();12931294        Self::add_token_index(item.collection, current_index, owner.clone())?;12951296        <ItemListIndex>::insert(item.collection, current_index);1297        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);12981299        // Add current block1300        let block_number: T::BlockNumber = 0.into();1301        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);13021303        // Update balance1304        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1305            .checked_add(value)1306            .unwrap();1307        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);13081309        Ok(())1310    }13111312    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1313        let current_index = <ItemListIndex>::get(item.collection)1314            .checked_add(1)1315            .expect("Item list index id error");13161317        let item_owner = item.owner.clone();1318        let collection_id = item.collection.clone();1319        Self::add_token_index(collection_id, current_index, item.owner.clone())?;13201321        <ItemListIndex>::insert(collection_id, current_index);1322        <NftItemList<T>>::insert(collection_id, current_index, item);13231324        // Add current block1325        let block_number: T::BlockNumber = 0.into();1326        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);13271328        // Update balance1329        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1330            .checked_add(1)1331            .unwrap();1332        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);13331334        Ok(())1335    }13361337    fn burn_refungible_item(1338        collection_id: u64,1339        item_id: u64,1340        owner: T::AccountId,1341    ) -> DispatchResult {1342        ensure!(1343            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1344            "Item does not exists"1345        );1346        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1347        let item = collection1348            .owner1349            .iter()1350            .filter(|&i| i.owner == owner)1351            .next()1352            .unwrap();1353        Self::remove_token_index(collection_id, item_id, owner.clone())?;13541355        // remove approve list1356        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));13571358        // update balance1359        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1360            .checked_sub(item.fraction as u64)1361            .unwrap();1362        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);13631364        <ReFungibleItemList<T>>::remove(collection_id, item_id);13651366        Ok(())1367    }13681369    fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1370        ensure!(1371            <NftItemList<T>>::contains_key(collection_id, item_id),1372            "Item does not exists"1373        );1374        let item = <NftItemList<T>>::get(collection_id, item_id);1375        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;13761377        // remove approve list1378        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));13791380        // update balance1381        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1382            .checked_sub(1)1383            .unwrap();1384        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1385        <NftItemList<T>>::remove(collection_id, item_id);13861387        Ok(())1388    }13891390    fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1391        ensure!(1392            <FungibleItemList<T>>::contains_key(collection_id, item_id),1393            "Item does not exists"1394        );1395        let item = <FungibleItemList<T>>::get(collection_id, item_id);1396        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;13971398        // remove approve list1399        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));14001401        // update balance1402        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1403            .checked_sub(item.value as u64)1404            .unwrap();1405        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);14061407        <FungibleItemList<T>>::remove(collection_id, item_id);14081409        Ok(())1410    }14111412    fn collection_exists(collection_id: u64) -> DispatchResult {1413        ensure!(1414            <Collection<T>>::contains_key(collection_id),1415            "This collection does not exist"1416        );1417        Ok(())1418    }14191420    fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1421        Self::collection_exists(collection_id)?;14221423        let target_collection = <Collection<T>>::get(collection_id);1424        ensure!(1425            subject == target_collection.owner,1426            "You do not own this collection"1427        );14281429        Ok(())1430    }14311432    fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1433        let target_collection = <Collection<T>>::get(collection_id);1434        let mut result: bool = subject == target_collection.owner;1435        let exists = <AdminList<T>>::contains_key(collection_id);14361437        if !result & exists {1438            if <AdminList<T>>::get(collection_id).contains(&subject) {1439                result = true1440            }1441        }14421443        result1444    }14451446    fn check_owner_or_admin_permissions(1447        collection_id: u64,1448        subject: T::AccountId,1449    ) -> DispatchResult {1450        Self::collection_exists(collection_id)?;1451        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());14521453        ensure!(1454            result,1455            "You do not have permissions to modify this collection"1456        );1457        Ok(())1458    }14591460    fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1461        let target_collection = <Collection<T>>::get(collection_id);14621463        match target_collection.mode {1464            CollectionMode::NFT => {1465                <NftItemList<T>>::get(collection_id, item_id).owner == subject1466            }1467            CollectionMode::Fungible(_) => {1468                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1469            }1470            CollectionMode::ReFungible(_) => {1471                <ReFungibleItemList<T>>::get(collection_id, item_id)1472                    .owner1473                    .iter()1474                    .any(|i| i.owner == subject)1475            }1476            CollectionMode::Invalid => false,1477        }1478    }14791480    fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1481        let mes = "Address is not in white list";1482        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1483        let wl = <WhiteList<T>>::get(collection_id);1484        ensure!(wl.contains(address), mes);14851486        Ok(())1487    }14881489    fn transfer_fungible(1490        collection_id: u64,1491        item_id: u64,1492        value: u64,1493        owner: T::AccountId,1494        new_owner: T::AccountId,1495    ) -> DispatchResult {1496        ensure!(1497            <FungibleItemList<T>>::contains_key(collection_id, item_id),1498            "Item not exists"1499        );15001501        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1502        let amount = full_item.value;15031504        ensure!(amount >= value.into(), "Item balance not enouth");15051506        // update balance1507        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1508            .checked_sub(value)1509            .unwrap();1510        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);15111512        let mut new_owner_account_id = 0;1513        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1514        if new_owner_items.len() > 0 {1515            new_owner_account_id = new_owner_items[0];1516        }15171518        let val64 = value.into();15191520        // transfer1521        if amount == val64 && new_owner_account_id == 0 {1522            // change owner1523            // new owner do not have account1524            let mut new_full_item = full_item.clone();1525            new_full_item.owner = new_owner.clone();1526            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);15271528            // update balance1529            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1530                .checked_add(value)1531                .unwrap();1532            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15331534            // update index collection1535            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1536        } else {1537            let mut new_full_item = full_item.clone();1538            new_full_item.value -= val64;15391540            // separate amount1541            if new_owner_account_id > 0 {1542                // new owner has account1543                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1544                item.value += val64;15451546                // update balance1547                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1548                    .checked_add(value)1549                    .unwrap();1550                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15511552                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1553            } else {1554                // new owner do not have account1555                let item = FungibleItemType {1556                    collection: collection_id,1557                    owner: new_owner.clone(),1558                    value: val64,1559                };15601561                Self::add_fungible_item(item)?;1562            }15631564            if amount == val64 {1565                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;15661567                // remove approve list1568                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1569                <FungibleItemList<T>>::remove(collection_id, item_id);1570            }15711572            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1573        }15741575        Ok(())1576    }15771578    fn transfer_refungible(1579        collection_id: u64,1580        item_id: u64,1581        value: u64,1582        owner: T::AccountId,1583        new_owner: T::AccountId,1584    ) -> DispatchResult {1585        ensure!(1586            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1587            "Item not exists"1588        );15891590        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1591        let item = full_item1592            .owner1593            .iter()1594            .filter(|i| i.owner == owner)1595            .next()1596            .unwrap();1597        let amount = item.fraction;15981599        ensure!(amount >= value.into(), "Item balance not enouth");16001601        // update balance1602        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1603            .checked_sub(value)1604            .unwrap();1605        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);16061607        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1608            .checked_add(value)1609            .unwrap();1610        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16111612        let old_owner = item.owner.clone();1613        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1614        let val64 = value.into();16151616        // transfer1617        if amount == val64 && !new_owner_has_account {1618            // change owner1619            // new owner do not have account1620            let mut new_full_item = full_item.clone();1621            new_full_item1622                .owner1623                .iter_mut()1624                .find(|i| i.owner == owner)1625                .unwrap()1626                .owner = new_owner.clone();1627            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);16281629            // update index collection1630            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1631        } else {1632            let mut new_full_item = full_item.clone();1633            new_full_item1634                .owner1635                .iter_mut()1636                .find(|i| i.owner == owner)1637                .unwrap()1638                .fraction -= val64;16391640            // separate amount1641            if new_owner_has_account {1642                // new owner has account1643                new_full_item1644                    .owner1645                    .iter_mut()1646                    .find(|i| i.owner == new_owner)1647                    .unwrap()1648                    .fraction += val64;1649            } else {1650                // new owner do not have account1651                new_full_item.owner.push(Ownership {1652                    owner: new_owner.clone(),1653                    fraction: val64,1654                });1655                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1656            }16571658            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1659        }16601661        Ok(())1662    }16631664    fn transfer_nft(1665        collection_id: u64,1666        item_id: u64,1667        sender: T::AccountId,1668        new_owner: T::AccountId,1669    ) -> DispatchResult {1670        ensure!(1671            <NftItemList<T>>::contains_key(collection_id, item_id),1672            "Item not exists"1673        );16741675        let mut item = <NftItemList<T>>::get(collection_id, item_id);16761677        ensure!(1678            sender == item.owner,1679            "sender parameter and item owner must be equal"1680        );16811682        // update balance1683        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1684            .checked_sub(1)1685            .unwrap();1686        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);16871688        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1689            .checked_add(1)1690            .unwrap();1691        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16921693        // change owner1694        let old_owner = item.owner.clone();1695        item.owner = new_owner.clone();1696        <NftItemList<T>>::insert(collection_id, item_id, item);16971698        // update index collection1699        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;17001701        // reset approved list1702        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1703        Ok(())1704    }1705    1706    fn item_exists(1707        collection_id: u64,1708        item_id: u64,1709        mode: &CollectionMode1710    ) -> DispatchResult {1711        match mode {1712            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1713            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1714            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1715            _ => ()1716        };1717        1718        Ok(())1719    }17201721    fn set_re_fungible_variable_data(1722        collection_id: u64,1723        item_id: u64,1724        data: Vec<u8>1725    ) -> DispatchResult {1726        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);17271728        item.variable_data = data;17291730        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);17311732        Ok(())1733    }17341735    fn set_nft_variable_data(1736        collection_id: u64,1737        item_id: u64,1738        data: Vec<u8>1739    ) -> DispatchResult {1740        let mut item = <NftItemList<T>>::get(collection_id, item_id);1741        1742        item.variable_data = data;17431744        <NftItemList<T>>::insert(collection_id, item_id, item);1745        1746        Ok(())1747    }17481749    fn init_collection(item: &CollectionType<T::AccountId>) {1750        // check params1751        assert!(1752            item.decimal_points <= 4,1753            "decimal_points parameter must be lower than 4"1754        );1755        assert!(1756            item.name.len() <= 64,1757            "Collection name can not be longer than 63 char"1758        );1759        assert!(1760            item.name.len() <= 256,1761            "Collection description can not be longer than 255 char"1762        );1763        assert!(1764            item.token_prefix.len() <= 16,1765            "Token prefix can not be longer than 15 char"1766        );17671768        // Generate next collection ID1769        let next_id = CreatedCollectionCount::get()1770            .checked_add(1)1771            .expect("collection id error");17721773        CreatedCollectionCount::put(next_id);1774    }17751776    fn init_nft_token(item: &NftItemType<T::AccountId>) {1777        let current_index = <ItemListIndex>::get(item.collection)1778            .checked_add(1)1779            .expect("Item list index id error");17801781        let item_owner = item.owner.clone();1782        let collection_id = item.collection.clone();1783        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();17841785        <ItemListIndex>::insert(collection_id, current_index);17861787        // Update balance1788        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1789            .checked_add(1)1790            .unwrap();1791        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1792    }17931794    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1795        let current_index = <ItemListIndex>::get(item.collection)1796            .checked_add(1)1797            .expect("Item list index id error");1798        let owner = item.owner.clone();1799        let value = item.value as u64;18001801        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();18021803        <ItemListIndex>::insert(item.collection, current_index);18041805        // Update balance1806        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1807            .checked_add(value)1808            .unwrap();1809        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1810    }18111812    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1813        let current_index = <ItemListIndex>::get(item.collection)1814            .checked_add(1)1815            .expect("Item list index id error");18161817        let value = item.owner.first().unwrap().fraction as u64;1818        let owner = item.owner.first().unwrap().owner.clone();18191820        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();18211822        <ItemListIndex>::insert(item.collection, current_index);18231824        // Update balance1825        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1826            .checked_add(value)1827            .unwrap();1828        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1829    }18301831    fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {18321833        // add to account limit1834        if <AccountItemCount<T>>::contains_key(owner.clone()) {18351836            // bound Owned tokens by a single address1837            let count = <AccountItemCount<T>>::get(owner.clone());1838            ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");18391840            <AccountItemCount<T>>::insert(owner.clone(), 1841                count.checked_add(1).unwrap());1842        }1843        else {1844            <AccountItemCount<T>>::insert(owner.clone(), 1);1845        }18461847        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1848        if list_exists {1849            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1850            let item_contains = list.contains(&item_index.clone());18511852            if !item_contains {1853                list.push(item_index.clone());1854            }18551856            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1857        } else {1858            let mut itm = Vec::new();1859            itm.push(item_index.clone());1860            <AddressTokens<T>>::insert(collection_id, owner, itm);1861            1862        }18631864        Ok(())1865    }18661867    fn remove_token_index(1868        collection_id: u64,1869        item_index: u64,1870        owner: T::AccountId,1871    ) -> DispatchResult {18721873        // update counter1874        <AccountItemCount<T>>::insert(owner.clone(), 1875            <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());187618771878        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1879        if list_exists {1880            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1881            let item_contains = list.contains(&item_index.clone());18821883            if item_contains {1884                list.retain(|&item| item != item_index);1885                <AddressTokens<T>>::insert(collection_id, owner, list);1886            }1887        }18881889        Ok(())1890    }18911892    fn move_token_index(1893        collection_id: u64,1894        item_index: u64,1895        old_owner: T::AccountId,1896        new_owner: T::AccountId,1897    ) -> DispatchResult {1898        Self::remove_token_index(collection_id, item_index, old_owner)?;1899        Self::add_token_index(collection_id, item_index, new_owner)?;19001901        Ok(())1902    }1903}19041905////////////////////////////////////////////////////////////////////////////////////////////////////1906// Economic models1907// #region19081909/// Fee multiplier.1910pub type Multiplier = FixedU128;19111912type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1913    <T as system::Trait>::AccountId,1914>>::Balance;1915type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1916    <T as system::Trait>::AccountId,1917>>::NegativeImbalance;19181919/// Require the transactor pay for themselves and maybe include a tip to gain additional priority1920/// in the queue.1921#[derive(Encode, Decode, Clone, Eq, PartialEq)]1922pub struct ChargeTransactionPayment<T: transaction_payment::Trait + Send + Sync>(1923    #[codec(compact)] BalanceOf<T>,1924);19251926impl<T: Trait + transaction_payment::Trait + Send + Sync> sp_std::fmt::Debug1927    for ChargeTransactionPayment<T>1928{1929    #[cfg(feature = "std")]1930    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1931        write!(f, "ChargeTransactionPayment<{:?}>", self.0)1932    }1933    #[cfg(not(feature = "std"))]1934    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1935        Ok(())1936    }1937}19381939impl<T: Trait + transaction_payment::Trait + Send + Sync> ChargeTransactionPayment<T>1940where1941    T::Call:1942        Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>>,1943    BalanceOf<T>: Send + Sync + FixedPointOperand,1944{1945    /// utility constructor. Used only in client/factory code.1946    pub fn from(fee: BalanceOf<T>) -> Self {1947        Self(fee)1948    }19491950    pub fn traditional_fee(1951        len: usize,1952        info: &DispatchInfoOf<T::Call>,1953        tip: BalanceOf<T>,1954    ) -> BalanceOf<T>1955    where1956        T::Call: Dispatchable<Info = DispatchInfo>,1957    {1958        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)1959    }19601961    fn withdraw_fee(1962        &self,1963        who: &T::AccountId,1964        call: &T::Call,1965        info: &DispatchInfoOf<T::Call>,1966        len: usize,1967    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {1968        let tip = self.0;19691970        // Set fee based on call type. Creating collection costs 1 Unique.1971        // All other transactions have traditional fees so far1972        let fee = match call.is_sub_type() {1973            Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),1974            _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes1975                                                        // _ => <BalanceOf<T>>::from(100)1976        };19771978        // Determine who is paying transaction fee based on ecnomic model1979        // Parse call to extract collection ID and access collection sponsor1980        let sponsor: T::AccountId = match call.is_sub_type() {1981            Some(Call::create_item(collection_id, _properties, _owner)) => {1982                <Collection<T>>::get(collection_id).sponsor1983            }1984            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {1985                let _collection_mode = <Collection<T>>::get(collection_id).mode;19861987                // sponsor timeout1988                let sponsor_transfer = match _collection_mode {1989                    CollectionMode::NFT => {1990                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);1991                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1992                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1993                        if block_number >= limit_time {1994                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);1995                            true1996                        }1997                        else {1998                            false1999                        }2000                    }2001                    CollectionMode::Fungible(_) => {2002                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2003                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2004                        if basket.iter().any(|i| i.address == _new_owner.clone())2005                        {2006                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2007                            let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2008                            if block_number >= limit_time {2009                                basket.retain(|x| x.address == item.address);2010                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2011                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2012                                true2013                            }2014                            else {2015                                false2016                            }2017                        }2018                        else {2019                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2020                            true2021                        }2022                    }2023                    CollectionMode::ReFungible(_) => {2024                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2025                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2026                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2027                        if block_number >= limit_time {2028                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2029                            true2030                        } else {2031                            false2032                        }2033                    }2034                    _ => {2035                        false2036                    },2037                };20382039                if !sponsor_transfer {2040                    T::AccountId::default()2041                } else {2042                    <Collection<T>>::get(collection_id).sponsor2043                }2044            }20452046            _ => T::AccountId::default(),2047        };20482049        let mut who_pays_fee: T::AccountId = sponsor.clone();2050        if sponsor == T::AccountId::default() {2051            who_pays_fee = who.clone();2052        }20532054        // Only mess with balances if fee is not zero.2055        if fee.is_zero() {2056            return Ok((fee, None));2057        }20582059        match <T as transaction_payment::Trait>::Currency::withdraw(2060            &who_pays_fee,2061            fee,2062            if tip.is_zero() {2063                WithdrawReason::TransactionPayment.into()2064            } else {2065                WithdrawReason::TransactionPayment | WithdrawReason::Tip2066            },2067            ExistenceRequirement::KeepAlive,2068        ) {2069            Ok(imbalance) => Ok((fee, Some(imbalance))),2070            Err(_) => Err(InvalidTransaction::Payment.into()),2071        }2072    }2073}20742075impl<T: Trait + transaction_payment::Trait + Send + Sync> SignedExtension2076    for ChargeTransactionPayment<T>2077where2078    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2079    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>>,2080{2081    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2082    type AccountId = T::AccountId;2083    type Call = T::Call;2084    type AdditionalSigned = ();2085    type Pre = (2086        BalanceOf<T>,2087        Self::AccountId,2088        Option<NegativeImbalanceOf<T>>,2089        BalanceOf<T>,2090    );2091    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2092        Ok(())2093    }20942095    fn validate(2096        &self,2097        who: &Self::AccountId,2098        call: &Self::Call,2099        info: &DispatchInfoOf<Self::Call>,2100        len: usize,2101    ) -> TransactionValidity {2102        let (fee, _) = self.withdraw_fee(who, call, info, len)?;21032104        let mut r = ValidTransaction::default();2105        // NOTE: we probably want to maximize the _fee (of any type) per weight unit_ here, which2106        // will be a bit more than setting the priority to tip. For now, this is enough.2107        r.priority = fee.saturated_into::<TransactionPriority>();2108        Ok(r)2109    }21102111    fn pre_dispatch(2112        self,2113        who: &Self::AccountId,2114        call: &Self::Call,2115        info: &DispatchInfoOf<Self::Call>,2116        len: usize,2117    ) -> Result<Self::Pre, TransactionValidityError> {2118        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2119        Ok((self.0, who.clone(), imbalance, fee))2120    }21212122    fn post_dispatch(2123        pre: Self::Pre,2124        info: &DispatchInfoOf<Self::Call>,2125        post_info: &PostDispatchInfoOf<Self::Call>,2126        len: usize,2127        _result: &DispatchResult,2128    ) -> Result<(), TransactionValidityError> {2129        let (tip, who, imbalance, fee) = pre;2130        if let Some(payed) = imbalance {2131            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2132                len as u32, info, post_info, tip,2133            );2134            let refund = fee.saturating_sub(actual_fee);2135            let actual_payment =2136                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2137                    &who, refund,2138                ) {2139                    Ok(refund_imbalance) => {2140                        // The refund cannot be larger than the up front payed max weight.2141                        // `PostDispatchInfo::calc_unspent` guards against such a case.2142                        match payed.offset(refund_imbalance) {2143                            Ok(actual_payment) => actual_payment,2144                            Err(_) => return Err(InvalidTransaction::Payment.into()),2145                        }2146                    }2147                    // We do not recreate the account using the refund. The up front payment2148                    // is gone in that case.2149                    Err(_) => payed,2150                };2151            let imbalances = actual_payment.split(tip);2152            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2153                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2154            );2155        }2156        Ok(())2157    }2158}2159// #endregion21602161
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,12    dispatch::DispatchResult,13    ensure, parameter_types, fail,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};25// use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight};2627use frame_system::{self as system, ensure_signed, ensure_root};28use sp_runtime::sp_std::prelude::Vec;29use sp_runtime::{30    traits::{31        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SignedExtension, Zero,32    },33    transaction_validity::{34        InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,35    },36    FixedPointOperand, FixedU128,37};38use pallet_contracts::ContractAddressFor;39use sp_runtime::traits::StaticLookup;4041#[cfg(test)]42mod mock;4344#[cfg(test)]45mod tests;4647mod default_weights;4849// Structs50// #region5152#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]53#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]54pub enum CollectionMode {55    Invalid,56    NFT,57    // decimal points58    Fungible(u32),59    // decimal points60    ReFungible(u32),61}6263impl Into<u8> for CollectionMode {64    fn into(self) -> u8 {65        match self {66            CollectionMode::Invalid => 0,67            CollectionMode::NFT => 1,68            CollectionMode::Fungible(_) => 2,69            CollectionMode::ReFungible(_) => 3,70        }71    }72}7374#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]75#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]76pub enum AccessMode {77    Normal,78    WhiteList,79}80impl Default for AccessMode {81    fn default() -> Self {82        Self::Normal83    }84}8586impl Default for CollectionMode {87    fn default() -> Self {88        Self::Invalid89    }90}9192#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]93#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]94pub struct Ownership<AccountId> {95    pub owner: AccountId,96    pub fraction: u128,97}9899#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]100#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]101pub struct CollectionType<AccountId> {102    pub owner: AccountId,103    pub mode: CollectionMode,104    pub access: AccessMode,105    pub decimal_points: u32,106    pub name: Vec<u16>,        // 64 include null escape char107    pub description: Vec<u16>, // 256 include null escape char108    pub token_prefix: Vec<u8>, // 16 include null escape char109    pub mint_mode: bool,110    pub offchain_schema: Vec<u8>,111    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender112    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship113    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 CollectionAdminsType<AccountId> {120    pub admin: AccountId,121    pub collection_id: u64,122}123124#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]125#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]126pub struct NftItemType<AccountId> {127    pub collection: u64,128    pub owner: AccountId,129    pub const_data: Vec<u8>,130    pub variable_data: Vec<u8>,131}132133#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]134#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]135pub struct FungibleItemType<AccountId> {136    pub collection: u64,137    pub owner: AccountId,138    pub value: u128,139}140141#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]142#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]143pub struct ReFungibleItemType<AccountId> {144    pub collection: u64,145    pub owner: Vec<Ownership<AccountId>>,146    pub const_data: Vec<u8>,147    pub variable_data: Vec<u8>,148}149150#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]151#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]152pub struct ApprovePermissions<AccountId> {153    pub approved: AccountId,154    pub amount: u64,155}156157#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]158#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]159pub struct VestingItem<AccountId, Moment> {160    pub sender: AccountId,161    pub recipient: AccountId,162    pub collection_id: u64,163    pub item_id: u64,164    pub amount: u64,165    pub vesting_date: Moment,166}167168#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]169#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170pub struct BasketItem<AccountId, BlockNumber> {171    pub address: AccountId,172    pub start_block: BlockNumber,173}174175#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]176#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]177pub struct ChainLimits {178    pub collection_numbers_limit: u64,179    pub account_token_ownership_limit: u64,180    pub collections_admins_limit: u64,181    pub custom_data_limit: u32,182183    // Timeouts for item types in passed blocks184    pub nft_sponsor_transfer_timeout: u32,185    pub fungible_sponsor_transfer_timeout: u32,186    pub refungible_sponsor_transfer_timeout: u32,187}188189pub trait WeightInfo {190	fn create_collection() -> Weight;191	fn destroy_collection() -> Weight;192	fn add_to_white_list() -> Weight;193	fn remove_from_white_list() -> Weight;194    fn set_public_access_mode() -> Weight;195    fn set_mint_permission() -> Weight;196    fn change_collection_owner() -> Weight;197    fn add_collection_admin() -> Weight;198    fn remove_collection_admin() -> Weight;199    fn set_collection_sponsor() -> Weight;200    fn confirm_sponsorship() -> Weight;201    fn remove_collection_sponsor() -> Weight;202    fn create_item(s: usize) -> Weight;203    fn burn_item() -> Weight;204    fn transfer() -> Weight;205    fn approve() -> Weight;206    fn transfer_from() -> Weight;207    fn set_offchain_schema() -> Weight;208    fn set_const_on_chain_schema() -> Weight;209    fn set_variable_on_chain_schema() -> Weight;210    fn set_variable_meta_data() -> Weight;211    // fn enable_contract_sponsoring() -> Weight;212}213214#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]215#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]216pub struct CreateNftData {217    pub const_data: Vec<u8>,218    pub variable_data: Vec<u8>,219}220221#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]222#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]223pub struct CreateFungibleData {224}225226#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]227#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]228pub struct CreateReFungibleData {229    pub const_data: Vec<u8>,230    pub variable_data: Vec<u8>,231}232233#[derive(Encode, Decode, Debug, Clone, PartialEq)]234#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]235pub enum CreateItemData {236    NFT(CreateNftData),237    Fungible(CreateFungibleData),238    ReFungible(CreateReFungibleData)239}240241impl CreateItemData {242    pub fn len(&self) -> usize {243        let len = match self {244            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),245            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),246            _ => 0247        };248        249        return len;250    }251}252253pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {254    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;255256    /// Weight information for extrinsics in this pallet.257	type WeightInfo: WeightInfo;258}259260#[cfg(feature = "runtime-benchmarks")]261mod benchmarking;262263// #endregion264265decl_storage! {266    trait Store for Module<T: Trait> as Nft {267268        // Private members269        NextCollectionID: u64;270        CreatedCollectionCount: u64;271        ChainVersion: u64;272        ItemListIndex: map hasher(blake2_128_concat) u64 => u64;273274        // Chain limits struct275        pub ChainLimit get(fn chain_limit) config(): ChainLimits;276277        // Bound counters278        CollectionCount: u64;279        pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;280281        // Basic collections282        pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;283        pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;284        pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;285286        /// Balance owner per collection map287        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;288289        /// second parameter: item id + owner account id290        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;291292        /// Item collections293        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;294        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;295        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;296297        /// Index list298        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;299300        /// Tokens transfer baskets301        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;302        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>>;303        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;304305        // Contract Sponsorship and Ownership306        pub ContractOwner get(fn contract_owner): map hasher(identity) T::AccountId => T::AccountId;307        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(identity) T::AccountId => bool;308    }309    add_extra_genesis {310        build(|config: &GenesisConfig<T>| {311            // Modification of storage312            for (_num, _c) in &config.collection {313                <Module<T>>::init_collection(_c);314            }315316            for (_num, _q, _i) in &config.nft_item_id {317                <Module<T>>::init_nft_token(_i);318            }319320            for (_num, _q, _i) in &config.fungible_item_id {321                <Module<T>>::init_fungible_token(_i);322            }323324            for (_num, _q, _i) in &config.refungible_item_id {325                <Module<T>>::init_refungible_token(_i);326            }327        })328    }329}330331decl_event!(332    pub enum Event<T>333    where334        AccountId = <T as system::Trait>::AccountId,335    {336        /// New collection was created337        /// 338        /// # Arguments339        /// 340        /// * collection_id: Globally unique identifier of newly created collection.341        /// 342        /// * mode: [CollectionMode] converted into u8.343        /// 344        /// * account_id: Collection owner.345        Created(u64, u8, AccountId),346347        /// New item was created.348        /// 349        /// # Arguments350        /// 351        /// * collection_id: Id of the collection where item was created.352        /// 353        /// * item_id: Id of an item. Unique within the collection.354        ItemCreated(u64, u64),355356        /// Collection item was burned.357        /// 358        /// # Arguments359        /// 360        /// collection_id.361        /// 362        /// item_id: Identifier of burned NFT.363        ItemDestroyed(u64, u64),364    }365);366367decl_module! {368    pub struct Module<T: Trait> for enum Call where origin: T::Origin {369370        fn deposit_event() = default;371372        fn on_initialize(now: T::BlockNumber) -> Weight {373374            if ChainVersion::get() < 2375            {376                let value = NextCollectionID::get();377                CreatedCollectionCount::put(value);378                ChainVersion::put(2);379            }380381            0382        }383384        /// 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.385        /// 386        /// # Permissions387        /// 388        /// * Anyone.389        /// 390        /// # Arguments391        /// 392        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.393        /// 394        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.395        /// 396        /// * token_prefix: UTF-8 string with token prefix.397        /// 398        /// * mode: [CollectionMode] collection type and type dependent data.399        // returns collection ID400        #[weight = T::WeightInfo::create_collection()]401        pub fn create_collection(origin,402                                 collection_name: Vec<u16>,403                                 collection_description: Vec<u16>,404                                 token_prefix: Vec<u8>,405                                 mode: CollectionMode) -> DispatchResult {406407            // Anyone can create a collection408            let who = ensure_signed(origin)?;409410            let decimal_points = match mode {411                CollectionMode::Fungible(points) => points,412                CollectionMode::ReFungible(points) => points,413                _ => 0414            };415416            // bound Total number of collections417            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");418419            // check params420            ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");421422            let mut name = collection_name.to_vec();423            name.push(0);424            ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");425426            let mut description = collection_description.to_vec();427            description.push(0);428            ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");429430            let mut prefix = token_prefix.to_vec();431            prefix.push(0);432            ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");433434            // Generate next collection ID435            let next_id = CreatedCollectionCount::get()436                .checked_add(1)437                .expect("collection id error");438439            // bound counter440            let total = CollectionCount::get()441                .checked_add(1)442                .expect("collection counter error");443444            CreatedCollectionCount::put(next_id);445            CollectionCount::put(total);446447            // Create new collection448            let new_collection = CollectionType {449                owner: who.clone(),450                name: name,451                mode: mode.clone(),452                mint_mode: false,453                access: AccessMode::Normal,454                description: description,455                decimal_points: decimal_points,456                token_prefix: prefix,457                offchain_schema: Vec::new(),458                sponsor: T::AccountId::default(),459                unconfirmed_sponsor: T::AccountId::default(),460                variable_on_chain_schema: Vec::new(),461                const_on_chain_schema: Vec::new(),462            };463464            // Add new collection to map465            <Collection<T>>::insert(next_id, new_collection);466467            // call event468            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));469470            Ok(())471        }472473        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.474        ///     475        /// # Permissions476        /// 477        /// * Collection Owner.478        /// 479        /// # Arguments480        /// 481        /// * collection_id: collection to destroy.482        #[weight = T::WeightInfo::destroy_collection()]483        pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {484485            let sender = ensure_signed(origin)?;486            Self::check_owner_permissions(collection_id, sender)?;487488            <AddressTokens<T>>::remove_prefix(collection_id);489            <ApprovedList<T>>::remove_prefix(collection_id);490            <Balance<T>>::remove_prefix(collection_id);491            <ItemListIndex>::remove(collection_id);492            <AdminList<T>>::remove(collection_id);493            <Collection<T>>::remove(collection_id);494            <WhiteList<T>>::remove(collection_id);495496            <NftItemList<T>>::remove_prefix(collection_id);497            <FungibleItemList<T>>::remove_prefix(collection_id);498            <ReFungibleItemList<T>>::remove_prefix(collection_id);499500            <NftTransferBasket<T>>::remove_prefix(collection_id);501            <FungibleTransferBasket<T>>::remove_prefix(collection_id);502            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);503504            if CollectionCount::get() > 0505            {506                // bound couter507                let total = CollectionCount::get()508                    .checked_sub(1)509                    .expect("collection counter error");510511                CollectionCount::put(total);512            }513514            Ok(())515        }516517        /// Add an address to white list.518        /// 519        /// # Permissions520        /// 521        /// * Collection Owner522        /// * Collection Admin523        /// 524        /// # Arguments525        /// 526        /// * collection_id.527        /// 528        /// * address.529        #[weight = T::WeightInfo::add_to_white_list()]530        pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{531532            let sender = ensure_signed(origin)?;533            Self::check_owner_or_admin_permissions(collection_id, sender)?;534535            let mut white_list_collection: Vec<T::AccountId>;536            if <WhiteList<T>>::contains_key(collection_id) {537                white_list_collection = <WhiteList<T>>::get(collection_id);538                if !white_list_collection.contains(&address.clone())539                {540                    white_list_collection.push(address.clone());541                }542            }543            else {544                white_list_collection = Vec::new();545                white_list_collection.push(address.clone());546            }547548            <WhiteList<T>>::insert(collection_id, white_list_collection);549            Ok(())550        }551552        /// Remove an address from white list.553        /// 554        /// # Permissions555        /// 556        /// * Collection Owner557        /// * Collection Admin558        /// 559        /// # Arguments560        /// 561        /// * collection_id.562        /// 563        /// * address.564        #[weight = T::WeightInfo::remove_from_white_list()]565        pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{566567            let sender = ensure_signed(origin)?;568            Self::check_owner_or_admin_permissions(collection_id, sender)?;569570            if <WhiteList<T>>::contains_key(collection_id) {571                let mut white_list_collection = <WhiteList<T>>::get(collection_id);572                if white_list_collection.contains(&address.clone())573                {574                    white_list_collection.retain(|i| *i != address.clone());575                    <WhiteList<T>>::insert(collection_id, white_list_collection);576                }577            }578579            Ok(())580        }581582        /// Toggle between normal and white list access for the methods with access for `Anyone`.583        /// 584        /// # Permissions585        /// 586        /// * Collection Owner.587        /// 588        /// # Arguments589        /// 590        /// * collection_id.591        /// 592        /// * mode: [AccessMode]593        #[weight = T::WeightInfo::set_public_access_mode()]594        pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult595        {596            let sender = ensure_signed(origin)?;597598            Self::check_owner_permissions(collection_id, sender)?;599            let mut target_collection = <Collection<T>>::get(collection_id);600            target_collection.access = mode;601            <Collection<T>>::insert(collection_id, target_collection);602603            Ok(())604        }605606        /// Allows Anyone to create tokens if:607        /// * White List is enabled, and608        /// * Address is added to white list, and609        /// * This method was called with True parameter610        /// 611        /// # Permissions612        /// * Collection Owner613        ///614        /// # Arguments615        /// 616        /// * collection_id.617        /// 618        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.619        #[weight = T::WeightInfo::set_mint_permission()]620        pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult621        {622            let sender = ensure_signed(origin)?;623624            Self::check_owner_permissions(collection_id, sender)?;625            let mut target_collection = <Collection<T>>::get(collection_id);626            target_collection.mint_mode = mint_permission;627            <Collection<T>>::insert(collection_id, target_collection);628629            Ok(())630        }631632        /// Change the owner of the collection.633        /// 634        /// # Permissions635        /// 636        /// * Collection Owner.637        /// 638        /// # Arguments639        /// 640        /// * collection_id.641        /// 642        /// * new_owner.643        #[weight = T::WeightInfo::change_collection_owner()]644        pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {645646            let sender = ensure_signed(origin)?;647            Self::check_owner_permissions(collection_id, sender)?;648            let mut target_collection = <Collection<T>>::get(collection_id);649            target_collection.owner = new_owner;650            <Collection<T>>::insert(collection_id, target_collection);651652            Ok(())653        }654655        /// Adds an admin of the Collection.656        /// 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. 657        /// 658        /// # Permissions659        /// 660        /// * Collection Owner.661        /// * Collection Admin.662        /// 663        /// # Arguments664        /// 665        /// * collection_id: ID of the Collection to add admin for.666        /// 667        /// * new_admin_id: Address of new admin to add.668        #[weight = T::WeightInfo::add_collection_admin()]669        pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {670671            let sender = ensure_signed(origin)?;672            Self::check_owner_or_admin_permissions(collection_id, sender)?;673            let mut admin_arr: Vec<T::AccountId> = Vec::new();674675            if <AdminList<T>>::contains_key(collection_id)676            {677                admin_arr = <AdminList<T>>::get(collection_id);678                ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");679            }680681            // Number of collection admins682            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");683684            admin_arr.push(new_admin_id);685            <AdminList<T>>::insert(collection_id, admin_arr);686687            Ok(())688        }689690        /// 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.691        ///692        /// # Permissions693        /// 694        /// * Collection Owner.695        /// * Collection Admin.696        /// 697        /// # Arguments698        /// 699        /// * collection_id: ID of the Collection to remove admin for.700        /// 701        /// * account_id: Address of admin to remove.702        #[weight = T::WeightInfo::remove_collection_admin()]703        pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {704705            let sender = ensure_signed(origin)?;706            Self::check_owner_or_admin_permissions(collection_id, sender)?;707708            if <AdminList<T>>::contains_key(collection_id)709            {710                let mut admin_arr = <AdminList<T>>::get(collection_id);711                admin_arr.retain(|i| *i != account_id);712                <AdminList<T>>::insert(collection_id, admin_arr);713            }714715            Ok(())716        }717718        /// # Permissions719        /// 720        /// * Collection Owner721        /// 722        /// # Arguments723        /// 724        /// * collection_id.725        /// 726        /// * new_sponsor.727        #[weight = T::WeightInfo::set_collection_sponsor()]728        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {729730            let sender = ensure_signed(origin)?;731            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");732733            let mut target_collection = <Collection<T>>::get(collection_id);734            ensure!(sender == target_collection.owner, "You do not own this collection");735736            target_collection.unconfirmed_sponsor = new_sponsor;737            <Collection<T>>::insert(collection_id, target_collection);738739            Ok(())740        }741742        /// # Permissions743        /// 744        /// * Sponsor.745        /// 746        /// # Arguments747        /// 748        /// * collection_id.749        #[weight = T::WeightInfo::confirm_sponsorship()]750        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {751752            let sender = ensure_signed(origin)?;753            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");754755            let mut target_collection = <Collection<T>>::get(collection_id);756            ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");757758            target_collection.sponsor = target_collection.unconfirmed_sponsor;759            target_collection.unconfirmed_sponsor = T::AccountId::default();760            <Collection<T>>::insert(collection_id, target_collection);761762            Ok(())763        }764765        /// Switch back to pay-per-own-transaction model.766        ///767        /// # Permissions768        ///769        /// * Collection owner.770        /// 771        /// # Arguments772        /// 773        /// * collection_id.774        #[weight = T::WeightInfo::remove_collection_sponsor()]775        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {776777            let sender = ensure_signed(origin)?;778            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");779780            let mut target_collection = <Collection<T>>::get(collection_id);781            ensure!(sender == target_collection.owner, "You do not own this collection");782783            target_collection.sponsor = T::AccountId::default();784            <Collection<T>>::insert(collection_id, target_collection);785786            Ok(())787        }788789        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.790        /// 791        /// # Permissions792        /// 793        /// * Collection Owner.794        /// * Collection Admin.795        /// * Anyone if796        ///     * White List is enabled, and797        ///     * Address is added to white list, and798        ///     * MintPermission is enabled (see SetMintPermission method)799        /// 800        /// # Arguments801        /// 802        /// * collection_id: ID of the collection.803        /// 804        /// * owner: Address, initial owner of the NFT.805        ///806        /// * data: Token data to store on chain.807        // #[weight =808        // (130_000_000 as Weight)809        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))810        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))811        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]812813        #[weight = T::WeightInfo::create_item(data.len())]814        pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {815816            let sender = ensure_signed(origin)?;817            Self::collection_exists(collection_id)?;818            let target_collection = <Collection<T>>::get(collection_id);819820            if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {821                ensure!(target_collection.mint_mode == true, "Public minting is not allowed for this collection.");822                Self::check_white_list(collection_id, &owner)?;823                Self::check_white_list(collection_id, &sender)?;824            }825826            match target_collection.mode827            {828                CollectionMode::NFT => {829                    if let CreateItemData::NFT(data) = data {830                        // check sizes831                        ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");832                        ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");833    834                        // Create nft item835                        let item = NftItemType {836                            collection: collection_id,837                            owner: owner,838                            const_data: data.const_data.clone(),839                            variable_data: data.variable_data.clone() 840                        };841    842                        Self::add_nft_item(item)?;843                    844                    } else {845                        fail!("Not NFT item data used to mint in NFT collection.");846                    }847                },848                CollectionMode::Fungible(_) => {849                    if let CreateItemData::Fungible(_) = data {850    851                        let item = FungibleItemType {852                            collection: collection_id,853                            owner: owner,854                            value: (10 as u128).pow(target_collection.decimal_points)855                        };856    857                        Self::add_fungible_item(item)?;858                    } else {859                        fail!("Not Fungible item data used to mint in Fungible collection.");860                    }861                },862                CollectionMode::ReFungible(_) => {863                    if let CreateItemData::ReFungible(data) = data {864    865                        // check sizes866                        ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");867                        ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");868    869                        let mut owner_list = Vec::new();870                        let value = (10 as u128).pow(target_collection.decimal_points);871                        owner_list.push(Ownership {owner: owner.clone(), fraction: value});872    873                        let item = ReFungibleItemType {874                            collection: collection_id,875                            owner: owner_list,876                            const_data: data.const_data.clone(),877                            variable_data: data.variable_data.clone() 878                        };879    880                        Self::add_refungible_item(item)?;881                    } else {882                        fail!("Not Re Fungible item data used to mint in Re Fungible collection.");883                    }884                },885                _ => { ensure!(1 == 0,"Unexpected collection type."); }886            };887888            // call event889            Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));890891            Ok(())892        }893894        /// Destroys a concrete instance of NFT.895        /// 896        /// # Permissions897        /// 898        /// * Collection Owner.899        /// * Collection Admin.900        /// * Current NFT Owner.901        /// 902        /// # Arguments903        /// 904        /// * collection_id: ID of the collection.905        /// 906        /// * item_id: ID of NFT to burn.907        #[weight = T::WeightInfo::burn_item()]908        pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {909910            let sender = ensure_signed(origin)?;911            Self::collection_exists(collection_id)?;912913            // Transfer permissions check914            let target_collection = <Collection<T>>::get(collection_id);915            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||916                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),917                "Only item owner, collection owner and admins can modify item");918919            if target_collection.access == AccessMode::WhiteList {920                Self::check_white_list(collection_id, &sender)?;921            }922923            match target_collection.mode924            {925                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,926                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,927                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,928                _ => ()929            };930931            // call event932            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));933934            Ok(())935        }936937        /// Change ownership of the token.938        /// 939        /// # Permissions940        /// 941        /// * Collection Owner942        /// * Collection Admin943        /// * Current NFT owner944        ///945        /// # Arguments946        /// 947        /// * recipient: Address of token recipient.948        /// 949        /// * collection_id.950        /// 951        /// * item_id: ID of the item952        ///     * Non-Fungible Mode: Required.953        ///     * Fungible Mode: Ignored.954        ///     * Re-Fungible Mode: Required.955        /// 956        /// * value: Amount to transfer.957        ///     * Non-Fungible Mode: Ignored958        ///     * Fungible Mode: Must specify transferred amount959        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)960        #[weight = T::WeightInfo::transfer()]961        pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {962963            let sender = ensure_signed(origin)?;964965            // Transfer permissions check966            let target_collection = <Collection<T>>::get(collection_id);967            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||968                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),969                "Only item owner, collection owner and admins can modify item");970971            if target_collection.access == AccessMode::WhiteList {972                Self::check_white_list(collection_id, &sender)?;973                Self::check_white_list(collection_id, &recipient)?;974            }975976            match target_collection.mode977            {978                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,979                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,980                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,981                _ => ()982            };983984            Ok(())985        }986987        /// Set, change, or remove approved address to transfer the ownership of the NFT.988        /// 989        /// # Permissions990        /// 991        /// * Collection Owner992        /// * Collection Admin993        /// * Current NFT owner994        /// 995        /// # Arguments996        /// 997        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).998        /// 999        /// * collection_id.1000        /// 1001        /// * item_id: ID of the item.1002        #[weight = T::WeightInfo::approve()]1003        pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {10041005            let sender = ensure_signed(origin)?;10061007            // Transfer permissions check1008            let target_collection = <Collection<T>>::get(collection_id);1009            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1010                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1011                "Only item owner, collection owner and admins can approve");10121013            if target_collection.access == AccessMode::WhiteList {1014                Self::check_white_list(collection_id, &sender)?;1015                Self::check_white_list(collection_id, &approved)?;1016            }10171018            // amount param stub1019            let amount = 100000000;10201021            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1022            if list_exists {10231024                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1025                let item_contains = list.iter().any(|i| i.approved == approved);10261027                if !item_contains {1028                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1029                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1030                }1031            } else {10321033                let mut list = Vec::new();1034                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1035                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1036            }10371038            Ok(())1039        }1040        1041        /// 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.1042        /// 1043        /// # Permissions1044        /// * Collection Owner1045        /// * Collection Admin1046        /// * Current NFT owner1047        /// * Address approved by current NFT owner1048        /// 1049        /// # Arguments1050        /// 1051        /// * from: Address that owns token.1052        /// 1053        /// * recipient: Address of token recipient.1054        /// 1055        /// * collection_id.1056        /// 1057        /// * item_id: ID of the item.1058        /// 1059        /// * value: Amount to transfer.1060        #[weight = T::WeightInfo::transfer_from()]1061        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10621063            let sender = ensure_signed(origin)?;1064            let mut appoved_transfer = false;10651066            // Check approve1067            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1068                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1069                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1070                if opt_item.is_some()1071                {1072                    appoved_transfer = true;1073                    ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1074                }1075            }10761077            // Transfer permissions check1078            let target_collection = <Collection<T>>::get(collection_id);1079            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1080                "Only item owner, collection owner and admins can modify items");10811082            if target_collection.access == AccessMode::WhiteList {1083                Self::check_white_list(collection_id, &sender)?;1084                Self::check_white_list(collection_id, &recipient)?;1085            }10861087            // remove approve1088            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1089                .into_iter().filter(|i| i.approved != sender.clone()).collect();1090            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);109110921093            match target_collection.mode1094            {1095                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1096                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1097                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1098                _ => ()1099            };11001101            Ok(())1102        }11031104        ///1105        #[weight = 0]1106        pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11071108            // let no_perm_mes = "You do not have permissions to modify this collection";1109            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1110            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1111            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11121113            // // on_nft_received  call11141115            // Self::transfer(origin, collection_id, item_id, new_owner)?;11161117            Ok(())1118        }1119        1120        /// Set off-chain data schema.1121        /// 1122        /// # Permissions1123        /// 1124        /// * Collection Owner1125        /// * Collection Admin1126        /// 1127        /// # Arguments1128        /// 1129        /// * collection_id.1130        /// 1131        /// * schema: String representing the offchain data schema.1132        #[weight = T::WeightInfo::set_variable_meta_data()]1133        pub fn set_variable_meta_data (1134            origin,1135            collection_id: u64,1136            item_id: u64,1137            data: Vec<u8>1138        ) -> DispatchResult {1139            let sender = ensure_signed(origin)?;1140            1141            Self::collection_exists(collection_id)?;11421143            // Modify permissions check1144            let target_collection = <Collection<T>>::get(collection_id);1145            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1146                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1147                "Only item owner, collection owner and admins can modify item");11481149            Self::item_exists(collection_id, item_id, &target_collection.mode)?;11501151            match target_collection.mode1152            {1153                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1154                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1155                _ => ()1156            };11571158            Ok(())1159        }1160        11611162        /// Set off-chain data schema.1163        /// 1164        /// # Permissions1165        /// 1166        /// * Collection Owner1167        /// * Collection Admin1168        /// 1169        /// # Arguments1170        /// 1171        /// * collection_id.1172        /// 1173        /// * schema: String representing the offchain data schema.1174        #[weight = T::WeightInfo::set_offchain_schema()]1175        pub fn set_offchain_schema(1176            origin,1177            collection_id: u64,1178            schema: Vec<u8>1179        ) -> DispatchResult {1180            let sender = ensure_signed(origin)?;1181            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;11821183            let mut target_collection = <Collection<T>>::get(collection_id);1184            target_collection.offchain_schema = schema;1185            <Collection<T>>::insert(collection_id, target_collection);11861187            Ok(())1188        }11891190        /// Set const on-chain data schema.1191        /// 1192        /// # Permissions1193        /// 1194        /// * Collection Owner1195        /// * Collection Admin1196        /// 1197        /// # Arguments1198        /// 1199        /// * collection_id.1200        /// 1201        /// * schema: String representing the const on-chain data schema.1202        #[weight = T::WeightInfo::set_const_on_chain_schema()]1203        pub fn set_const_on_chain_schema (1204            origin,1205            collection_id: u64,1206            schema: Vec<u8>1207        ) -> DispatchResult {1208            let sender = ensure_signed(origin)?;1209            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12101211            let mut target_collection = <Collection<T>>::get(collection_id);1212            target_collection.const_on_chain_schema = schema;1213            <Collection<T>>::insert(collection_id, target_collection);12141215            Ok(())1216        }12171218        /// Set variable on-chain data schema.1219        /// 1220        /// # Permissions1221        /// 1222        /// * Collection Owner1223        /// * Collection Admin1224        /// 1225        /// # Arguments1226        /// 1227        /// * collection_id.1228        /// 1229        /// * schema: String representing the variable on-chain data schema.1230        #[weight = T::WeightInfo::set_const_on_chain_schema()]1231        pub fn set_variable_on_chain_schema (1232            origin,1233            collection_id: u64,1234            schema: Vec<u8>1235        ) -> DispatchResult {1236            let sender = ensure_signed(origin)?;1237            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12381239            let mut target_collection = <Collection<T>>::get(collection_id);1240            target_collection.variable_on_chain_schema = schema;1241            <Collection<T>>::insert(collection_id, target_collection);12421243            Ok(())1244        }12451246        // Sudo permissions function1247        #[weight = 0]1248        pub fn set_chain_limits(1249            origin,1250            limits: ChainLimits1251        ) -> DispatchResult {1252            ensure_root(origin)?;1253            <ChainLimit>::put(limits);1254            Ok(())1255        }12561257        /// Enable smart contract self-sponsoring.1258        /// 1259        /// # Permissions1260        /// 1261        /// * Contract Owner1262        /// 1263        /// # Arguments1264        /// 1265        /// * contract address1266        /// * enable flag1267        /// 1268        #[weight = 0]1269        pub fn enable_contract_sponsoring(1270            origin,1271            contract_address: T::AccountId,1272            enable: bool1273        ) -> DispatchResult {1274            let sender = ensure_signed(origin)?;1275            let mut is_owner = false;1276            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1277                let owner = <ContractOwner<T>>::get(&contract_address);1278                is_owner = sender == owner;1279            }1280            ensure!(is_owner, "Only contract owner may call this method");12811282            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1283            Ok(())1284        }12851286    }1287}12881289impl<T: Trait> Module<T> {1290    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1291        let current_index = <ItemListIndex>::get(item.collection)1292            .checked_add(1)1293            .expect("Item list index id error");1294        let itemcopy = item.clone();1295        let owner = item.owner.clone();1296        let value = item.value as u64;12971298        Self::add_token_index(item.collection, current_index, owner.clone())?;12991300        <ItemListIndex>::insert(item.collection, current_index);1301        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);13021303        // Add current block1304        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1305        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1306        1307        // Update balance1308        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1309            .checked_add(value)1310            .unwrap();1311        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);13121313        Ok(())1314    }13151316    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1317        let current_index = <ItemListIndex>::get(item.collection)1318            .checked_add(1)1319            .expect("Item list index id error");1320        let itemcopy = item.clone();13211322        let value = item.owner.first().unwrap().fraction as u64;1323        let owner = item.owner.first().unwrap().owner.clone();13241325        Self::add_token_index(item.collection, current_index, owner.clone())?;13261327        <ItemListIndex>::insert(item.collection, current_index);1328        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);13291330        // Add current block1331        let block_number: T::BlockNumber = 0.into();1332        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);13331334        // Update balance1335        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1336            .checked_add(value)1337            .unwrap();1338        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);13391340        Ok(())1341    }13421343    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1344        let current_index = <ItemListIndex>::get(item.collection)1345            .checked_add(1)1346            .expect("Item list index id error");13471348        let item_owner = item.owner.clone();1349        let collection_id = item.collection.clone();1350        Self::add_token_index(collection_id, current_index, item.owner.clone())?;13511352        <ItemListIndex>::insert(collection_id, current_index);1353        <NftItemList<T>>::insert(collection_id, current_index, item);13541355        // Add current block1356        let block_number: T::BlockNumber = 0.into();1357        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);13581359        // Update balance1360        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1361            .checked_add(1)1362            .unwrap();1363        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);13641365        Ok(())1366    }13671368    fn burn_refungible_item(1369        collection_id: u64,1370        item_id: u64,1371        owner: T::AccountId,1372    ) -> DispatchResult {1373        ensure!(1374            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1375            "Item does not exists"1376        );1377        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1378        let item = collection1379            .owner1380            .iter()1381            .filter(|&i| i.owner == owner)1382            .next()1383            .unwrap();1384        Self::remove_token_index(collection_id, item_id, owner.clone())?;13851386        // remove approve list1387        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));13881389        // update balance1390        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1391            .checked_sub(item.fraction as u64)1392            .unwrap();1393        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);13941395        <ReFungibleItemList<T>>::remove(collection_id, item_id);13961397        Ok(())1398    }13991400    fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1401        ensure!(1402            <NftItemList<T>>::contains_key(collection_id, item_id),1403            "Item does not exists"1404        );1405        let item = <NftItemList<T>>::get(collection_id, item_id);1406        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;14071408        // remove approve list1409        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));14101411        // update balance1412        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1413            .checked_sub(1)1414            .unwrap();1415        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1416        <NftItemList<T>>::remove(collection_id, item_id);14171418        Ok(())1419    }14201421    fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1422        ensure!(1423            <FungibleItemList<T>>::contains_key(collection_id, item_id),1424            "Item does not exists"1425        );1426        let item = <FungibleItemList<T>>::get(collection_id, item_id);1427        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;14281429        // remove approve list1430        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));14311432        // update balance1433        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1434            .checked_sub(item.value as u64)1435            .unwrap();1436        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);14371438        <FungibleItemList<T>>::remove(collection_id, item_id);14391440        Ok(())1441    }14421443    fn collection_exists(collection_id: u64) -> DispatchResult {1444        ensure!(1445            <Collection<T>>::contains_key(collection_id),1446            "This collection does not exist"1447        );1448        Ok(())1449    }14501451    fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1452        Self::collection_exists(collection_id)?;14531454        let target_collection = <Collection<T>>::get(collection_id);1455        ensure!(1456            subject == target_collection.owner,1457            "You do not own this collection"1458        );14591460        Ok(())1461    }14621463    fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1464        let target_collection = <Collection<T>>::get(collection_id);1465        let mut result: bool = subject == target_collection.owner;1466        let exists = <AdminList<T>>::contains_key(collection_id);14671468        if !result & exists {1469            if <AdminList<T>>::get(collection_id).contains(&subject) {1470                result = true1471            }1472        }14731474        result1475    }14761477    fn check_owner_or_admin_permissions(1478        collection_id: u64,1479        subject: T::AccountId,1480    ) -> DispatchResult {1481        Self::collection_exists(collection_id)?;1482        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());14831484        ensure!(1485            result,1486            "You do not have permissions to modify this collection"1487        );1488        Ok(())1489    }14901491    fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1492        let target_collection = <Collection<T>>::get(collection_id);14931494        match target_collection.mode {1495            CollectionMode::NFT => {1496                <NftItemList<T>>::get(collection_id, item_id).owner == subject1497            }1498            CollectionMode::Fungible(_) => {1499                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1500            }1501            CollectionMode::ReFungible(_) => {1502                <ReFungibleItemList<T>>::get(collection_id, item_id)1503                    .owner1504                    .iter()1505                    .any(|i| i.owner == subject)1506            }1507            CollectionMode::Invalid => false,1508        }1509    }15101511    fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1512        let mes = "Address is not in white list";1513        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1514        let wl = <WhiteList<T>>::get(collection_id);1515        ensure!(wl.contains(address), mes);15161517        Ok(())1518    }15191520    fn transfer_fungible(1521        collection_id: u64,1522        item_id: u64,1523        value: u64,1524        owner: T::AccountId,1525        new_owner: T::AccountId,1526    ) -> DispatchResult {1527        ensure!(1528            <FungibleItemList<T>>::contains_key(collection_id, item_id),1529            "Item not exists"1530        );15311532        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1533        let amount = full_item.value;15341535        ensure!(amount >= value.into(), "Item balance not enouth");15361537        // update balance1538        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1539            .checked_sub(value)1540            .unwrap();1541        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);15421543        let mut new_owner_account_id = 0;1544        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1545        if new_owner_items.len() > 0 {1546            new_owner_account_id = new_owner_items[0];1547        }15481549        let val64 = value.into();15501551        // transfer1552        if amount == val64 && new_owner_account_id == 0 {1553            // change owner1554            // new owner do not have account1555            let mut new_full_item = full_item.clone();1556            new_full_item.owner = new_owner.clone();1557            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);15581559            // update balance1560            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1561                .checked_add(value)1562                .unwrap();1563            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15641565            // update index collection1566            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1567        } else {1568            let mut new_full_item = full_item.clone();1569            new_full_item.value -= val64;15701571            // separate amount1572            if new_owner_account_id > 0 {1573                // new owner has account1574                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1575                item.value += val64;15761577                // update balance1578                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1579                    .checked_add(value)1580                    .unwrap();1581                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15821583                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1584            } else {1585                // new owner do not have account1586                let item = FungibleItemType {1587                    collection: collection_id,1588                    owner: new_owner.clone(),1589                    value: val64,1590                };15911592                Self::add_fungible_item(item)?;1593            }15941595            if amount == val64 {1596                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;15971598                // remove approve list1599                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1600                <FungibleItemList<T>>::remove(collection_id, item_id);1601            }16021603            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1604        }16051606        Ok(())1607    }16081609    fn transfer_refungible(1610        collection_id: u64,1611        item_id: u64,1612        value: u64,1613        owner: T::AccountId,1614        new_owner: T::AccountId,1615    ) -> DispatchResult {1616        ensure!(1617            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1618            "Item not exists"1619        );16201621        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1622        let item = full_item1623            .owner1624            .iter()1625            .filter(|i| i.owner == owner)1626            .next()1627            .unwrap();1628        let amount = item.fraction;16291630        ensure!(amount >= value.into(), "Item balance not enouth");16311632        // update balance1633        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1634            .checked_sub(value)1635            .unwrap();1636        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);16371638        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1639            .checked_add(value)1640            .unwrap();1641        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16421643        let old_owner = item.owner.clone();1644        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1645        let val64 = value.into();16461647        // transfer1648        if amount == val64 && !new_owner_has_account {1649            // change owner1650            // new owner do not have account1651            let mut new_full_item = full_item.clone();1652            new_full_item1653                .owner1654                .iter_mut()1655                .find(|i| i.owner == owner)1656                .unwrap()1657                .owner = new_owner.clone();1658            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);16591660            // update index collection1661            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1662        } else {1663            let mut new_full_item = full_item.clone();1664            new_full_item1665                .owner1666                .iter_mut()1667                .find(|i| i.owner == owner)1668                .unwrap()1669                .fraction -= val64;16701671            // separate amount1672            if new_owner_has_account {1673                // new owner has account1674                new_full_item1675                    .owner1676                    .iter_mut()1677                    .find(|i| i.owner == new_owner)1678                    .unwrap()1679                    .fraction += val64;1680            } else {1681                // new owner do not have account1682                new_full_item.owner.push(Ownership {1683                    owner: new_owner.clone(),1684                    fraction: val64,1685                });1686                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1687            }16881689            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1690        }16911692        Ok(())1693    }16941695    fn transfer_nft(1696        collection_id: u64,1697        item_id: u64,1698        sender: T::AccountId,1699        new_owner: T::AccountId,1700    ) -> DispatchResult {1701        ensure!(1702            <NftItemList<T>>::contains_key(collection_id, item_id),1703            "Item not exists"1704        );17051706        let mut item = <NftItemList<T>>::get(collection_id, item_id);17071708        ensure!(1709            sender == item.owner,1710            "sender parameter and item owner must be equal"1711        );17121713        // update balance1714        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1715            .checked_sub(1)1716            .unwrap();1717        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);17181719        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1720            .checked_add(1)1721            .unwrap();1722        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17231724        // change owner1725        let old_owner = item.owner.clone();1726        item.owner = new_owner.clone();1727        <NftItemList<T>>::insert(collection_id, item_id, item);17281729        // update index collection1730        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;17311732        // reset approved list1733        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1734        Ok(())1735    }1736    1737    fn item_exists(1738        collection_id: u64,1739        item_id: u64,1740        mode: &CollectionMode1741    ) -> DispatchResult {1742        match mode {1743            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1744            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1745            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1746            _ => ()1747        };1748        1749        Ok(())1750    }17511752    fn set_re_fungible_variable_data(1753        collection_id: u64,1754        item_id: u64,1755        data: Vec<u8>1756    ) -> DispatchResult {1757        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);17581759        item.variable_data = data;17601761        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);17621763        Ok(())1764    }17651766    fn set_nft_variable_data(1767        collection_id: u64,1768        item_id: u64,1769        data: Vec<u8>1770    ) -> DispatchResult {1771        let mut item = <NftItemList<T>>::get(collection_id, item_id);1772        1773        item.variable_data = data;17741775        <NftItemList<T>>::insert(collection_id, item_id, item);1776        1777        Ok(())1778    }17791780    fn init_collection(item: &CollectionType<T::AccountId>) {1781        // check params1782        assert!(1783            item.decimal_points <= 4,1784            "decimal_points parameter must be lower than 4"1785        );1786        assert!(1787            item.name.len() <= 64,1788            "Collection name can not be longer than 63 char"1789        );1790        assert!(1791            item.name.len() <= 256,1792            "Collection description can not be longer than 255 char"1793        );1794        assert!(1795            item.token_prefix.len() <= 16,1796            "Token prefix can not be longer than 15 char"1797        );17981799        // Generate next collection ID1800        let next_id = CreatedCollectionCount::get()1801            .checked_add(1)1802            .expect("collection id error");18031804        CreatedCollectionCount::put(next_id);1805    }18061807    fn init_nft_token(item: &NftItemType<T::AccountId>) {1808        let current_index = <ItemListIndex>::get(item.collection)1809            .checked_add(1)1810            .expect("Item list index id error");18111812        let item_owner = item.owner.clone();1813        let collection_id = item.collection.clone();1814        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();18151816        <ItemListIndex>::insert(collection_id, current_index);18171818        // Update balance1819        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1820            .checked_add(1)1821            .unwrap();1822        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1823    }18241825    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1826        let current_index = <ItemListIndex>::get(item.collection)1827            .checked_add(1)1828            .expect("Item list index id error");1829        let owner = item.owner.clone();1830        let value = item.value as u64;18311832        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();18331834        <ItemListIndex>::insert(item.collection, current_index);18351836        // Update balance1837        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1838            .checked_add(value)1839            .unwrap();1840        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1841    }18421843    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1844        let current_index = <ItemListIndex>::get(item.collection)1845            .checked_add(1)1846            .expect("Item list index id error");18471848        let value = item.owner.first().unwrap().fraction as u64;1849        let owner = item.owner.first().unwrap().owner.clone();18501851        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();18521853        <ItemListIndex>::insert(item.collection, current_index);18541855        // Update balance1856        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1857            .checked_add(value)1858            .unwrap();1859        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1860    }18611862    fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {18631864        // add to account limit1865        if <AccountItemCount<T>>::contains_key(owner.clone()) {18661867            // bound Owned tokens by a single address1868            let count = <AccountItemCount<T>>::get(owner.clone());1869            ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");18701871            <AccountItemCount<T>>::insert(owner.clone(), 1872                count.checked_add(1).unwrap());1873        }1874        else {1875            <AccountItemCount<T>>::insert(owner.clone(), 1);1876        }18771878        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1879        if list_exists {1880            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1881            let item_contains = list.contains(&item_index.clone());18821883            if !item_contains {1884                list.push(item_index.clone());1885            }18861887            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1888        } else {1889            let mut itm = Vec::new();1890            itm.push(item_index.clone());1891            <AddressTokens<T>>::insert(collection_id, owner, itm);1892            1893        }18941895        Ok(())1896    }18971898    fn remove_token_index(1899        collection_id: u64,1900        item_index: u64,1901        owner: T::AccountId,1902    ) -> DispatchResult {19031904        // update counter1905        <AccountItemCount<T>>::insert(owner.clone(), 1906            <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());190719081909        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1910        if list_exists {1911            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1912            let item_contains = list.contains(&item_index.clone());19131914            if item_contains {1915                list.retain(|&item| item != item_index);1916                <AddressTokens<T>>::insert(collection_id, owner, list);1917            }1918        }19191920        Ok(())1921    }19221923    fn move_token_index(1924        collection_id: u64,1925        item_index: u64,1926        old_owner: T::AccountId,1927        new_owner: T::AccountId,1928    ) -> DispatchResult {1929        Self::remove_token_index(collection_id, item_index, old_owner)?;1930        Self::add_token_index(collection_id, item_index, new_owner)?;19311932        Ok(())1933    }1934}19351936////////////////////////////////////////////////////////////////////////////////////////////////////1937// Economic models1938// #region19391940/// Fee multiplier.1941pub type Multiplier = FixedU128;19421943type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1944    <T as system::Trait>::AccountId,1945>>::Balance;1946type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1947    <T as system::Trait>::AccountId,1948>>::NegativeImbalance;19491950/// Require the transactor pay for themselves and maybe include a tip to gain additional priority1951/// in the queue.1952#[derive(Encode, Decode, Clone, Eq, PartialEq)]1953pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(1954    #[codec(compact)] BalanceOf<T>1955);19561957impl<T: Trait + Send + Sync> sp_std::fmt::Debug1958    for ChargeTransactionPayment<T>1959{1960    #[cfg(feature = "std")]1961    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1962        write!(f, "ChargeTransactionPayment<{:?}>", self.0)1963    }1964    #[cfg(not(feature = "std"))]1965    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1966        Ok(())1967    }1968}19691970impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>1971where1972    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,1973    BalanceOf<T>: Send + Sync + FixedPointOperand,1974{1975    /// utility constructor. Used only in client/factory code.1976    pub fn from(fee: BalanceOf<T>) -> Self {1977        Self(fee)1978    }19791980    pub fn traditional_fee(1981        len: usize,1982        info: &DispatchInfoOf<T::Call>,1983        tip: BalanceOf<T>,1984    ) -> BalanceOf<T>1985    where1986        T::Call: Dispatchable<Info = DispatchInfo>,1987    {1988        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)1989    }19901991    fn withdraw_fee(1992        &self,1993        who: &T::AccountId,1994        call: &T::Call,1995        info: &DispatchInfoOf<T::Call>,1996        len: usize,1997    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {1998        let tip = self.0;19992000        // Set fee based on call type. Creating collection costs 1 Unique.2001        // All other transactions have traditional fees so far2002        // let fee = match call.is_sub_type() {2003        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2004        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2005        //                                                 // _ => <BalanceOf<T>>::from(100)2006        // };2007        let fee = Self::traditional_fee(len, info, tip);20082009        // Determine who is paying transaction fee based on ecnomic model2010        // Parse call to extract collection ID and access collection sponsor2011        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2012            Some(Call::create_item(collection_id, _properties, _owner)) => {2013                <Collection<T>>::get(collection_id).sponsor2014            }2015            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2016                let _collection_mode = <Collection<T>>::get(collection_id).mode;20172018                // sponsor timeout2019                let sponsor_transfer = match _collection_mode {2020                    CollectionMode::NFT => {2021                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2022                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2023                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2024                        if block_number >= limit_time {2025                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2026                            true2027                        }2028                        else {2029                            false2030                        }2031                    }2032                    CollectionMode::Fungible(_) => {2033                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2034                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2035                        if basket.iter().any(|i| i.address == _new_owner.clone())2036                        {2037                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2038                            let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2039                            if block_number >= limit_time {2040                                basket.retain(|x| x.address == item.address);2041                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2042                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2043                                true2044                            }2045                            else {2046                                false2047                            }2048                        }2049                        else {2050                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2051                            true2052                        }2053                    }2054                    CollectionMode::ReFungible(_) => {2055                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2056                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2057                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2058                        if block_number >= limit_time {2059                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2060                            true2061                        } else {2062                            false2063                        }2064                    }2065                    _ => {2066                        false2067                    },2068                };20692070                if !sponsor_transfer {2071                    T::AccountId::default()2072                } else {2073                    <Collection<T>>::get(collection_id).sponsor2074                }2075            }20762077            _ => T::AccountId::default(),2078        };20792080        // Sponsor smart contracts2081        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {20822083            // On instantiation: set the contract owner2084            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {20852086                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2087                    code_hash,2088                    &data,2089                    &who,2090                );2091                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());20922093                T::AccountId::default()2094            },20952096            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2097            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {20982099                let mut sp = T::AccountId::default();2100                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());2101                if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2102                    if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2103                        sp = called_contract;2104                    }2105                }21062107                sp2108            },21092110            _ => sponsor,2111        };21122113        let mut who_pays_fee: T::AccountId = sponsor.clone();2114        if sponsor == T::AccountId::default() {2115            who_pays_fee = who.clone();2116        }21172118        // Only mess with balances if fee is not zero.2119        if fee.is_zero() {2120            return Ok((fee, None));2121        }21222123        match <T as transaction_payment::Trait>::Currency::withdraw(2124            &who_pays_fee,2125            fee,2126            if tip.is_zero() {2127                WithdrawReason::TransactionPayment.into()2128            } else {2129                WithdrawReason::TransactionPayment | WithdrawReason::Tip2130            },2131            ExistenceRequirement::KeepAlive,2132        ) {2133            Ok(imbalance) => Ok((fee, Some(imbalance))),2134            Err(_) => Err(InvalidTransaction::Payment.into()),2135        }2136    }2137}213821392140impl<T: Trait + Send + Sync> SignedExtension2141    for ChargeTransactionPayment<T>2142where2143    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2144    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2145{2146    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2147    type AccountId = T::AccountId;2148    type Call = T::Call;2149    type AdditionalSigned = ();2150    type Pre = (2151        BalanceOf<T>,2152        Self::AccountId,2153        Option<NegativeImbalanceOf<T>>,2154        BalanceOf<T>,2155    );2156    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2157        Ok(())2158    }21592160    fn validate(2161        &self,2162        _who: &Self::AccountId,2163        _call: &Self::Call,2164        _info: &DispatchInfoOf<Self::Call>,2165        _len: usize,2166    ) -> TransactionValidity {2167        Ok(ValidTransaction::default())2168    }21692170    fn pre_dispatch(2171        self,2172        who: &Self::AccountId,2173        call: &Self::Call,2174        info: &DispatchInfoOf<Self::Call>,2175        len: usize,2176    ) -> Result<Self::Pre, TransactionValidityError> {2177        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2178        Ok((self.0, who.clone(), imbalance, fee))2179    }21802181    fn post_dispatch(2182        pre: Self::Pre,2183        info: &DispatchInfoOf<Self::Call>,2184        post_info: &PostDispatchInfoOf<Self::Call>,2185        len: usize,2186        _result: &DispatchResult,2187    ) -> Result<(), TransactionValidityError> {2188        let (tip, who, imbalance, fee) = pre;2189        if let Some(payed) = imbalance {2190            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2191                len as u32, info, post_info, tip,2192            );2193            let refund = fee.saturating_sub(actual_fee);2194            let actual_payment =2195                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2196                    &who, refund,2197                ) {2198                    Ok(refund_imbalance) => {2199                        // The refund cannot be larger than the up front payed max weight.2200                        // `PostDispatchInfo::calc_unspent` guards against such a case.2201                        match payed.offset(refund_imbalance) {2202                            Ok(actual_payment) => actual_payment,2203                            Err(_) => return Err(InvalidTransaction::Payment.into()),2204                        }2205                    }2206                    // We do not recreate the account using the refund. The up front payment2207                    // is gone in that case.2208                    Err(_) => payed,2209                };2210            let imbalances = actual_payment.split(tip);2211            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2212                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2213            );2214        }2215        Ok(())2216    }2217}22182219// #endregion22202221
modifiedpallets/nft/src/mock.rsdiffbeforeafterboth
--- a/pallets/nft/src/mock.rs
+++ b/pallets/nft/src/mock.rs
@@ -1,20 +1,27 @@
 // Creating mock runtime here
 
 use crate::{Module, Trait};
+
+use pallet_contracts::{
+	ContractAddressFor, TrieId, TrieIdGenerator,
+};
+
 use frame_support::{
     impl_outer_origin, parameter_types,
     weights::{
-        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight},
-        Weight,
+      //  constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight},
+        Weight, IdentityFee,
     },
 };
 use frame_system as system;
+use transaction_payment;
 use sp_core::H256;
 use sp_runtime::{
     testing::Header,
     traits::{BlakeTwo256, IdentityLookup, Saturating},
     Perbill,
 };
+pub use pallet_balances;
 
 impl_outer_origin! {
     pub enum Origin for Test {}
@@ -56,13 +63,101 @@
 	type AvailableBlockRatio = AvailableBlockRatio;
 	type Version = ();
 	type PalletInfo = ();
-	type AccountData = ();
+	type AccountData = pallet_balances::AccountData<u64>;
 	type OnNewAccount = ();
 	type OnKilledAccount = ();
 	type SystemWeightInfo = ();
 }
-impl Trait for Test {
+
+parameter_types! {
+	pub const ExistentialDeposit: u64 = 1;
+	pub const MaxLocks: u32 = 50;
+}
+
+type System = frame_system::Module<Test>;
+impl pallet_balances::Trait for Test {
+    type AccountStore = System;
+    type Balance = u64;
+    type DustRemoval = ();
     type Event = ();
+	type ExistentialDeposit = ExistentialDeposit;
+	type WeightInfo = ();
+	type MaxLocks = MaxLocks;
+}
+
+parameter_types! {
+	pub const TransactionByteFee: u64 = 1;
+}
+impl transaction_payment::Trait for Test {
+	type Currency = pallet_balances::Module<Test>;
+	type OnTransactionPayment = ();
+	type TransactionByteFee = TransactionByteFee;
+	type WeightToFee = IdentityFee<u64>;
+	type FeeMultiplierUpdate = ();
+}
+
+
+parameter_types! {
+	pub const MinimumPeriod: u64 = 1;
+}
+impl pallet_timestamp::Trait for Test {
+	type Moment = u64;
+	type OnTimestampSet = ();
+	type MinimumPeriod = MinimumPeriod;
+	type WeightInfo = ();
+}
+
+type Timestamp = pallet_timestamp::Module<Test>;
+type Randomness = pallet_randomness_collective_flip::Module<Test>;
+
+parameter_types! {
+	pub const TombstoneDeposit: u64 = 1;
+	pub const RentByteFee: u64 = 1;
+	pub const RentDepositOffset: u64 = 1;
+	pub const SurchargeReward: u64 = 1;
+}
+
+pub struct DummyTrieIdGenerator;
+impl TrieIdGenerator<u64> for DummyTrieIdGenerator {
+	fn trie_id(account_id: &u64) -> TrieId {
+		let new_seed = *account_id + 1;
+		let mut res = vec![];
+		res.extend_from_slice(&new_seed.to_le_bytes());
+		res.extend_from_slice(&account_id.to_le_bytes());
+		res
+	}
+}
+
+pub struct DummyContractAddressFor;
+impl ContractAddressFor<H256, u64> for DummyContractAddressFor {
+	fn contract_address_for(_code_hash: &H256, _data: &[u8], origin: &u64) -> u64 {
+		*origin + 1
+	}
+}
+
+impl pallet_contracts::Trait for Test {
+	type Time = Timestamp;
+	type Randomness = Randomness;
+	type Currency = pallet_balances::Module<Test>;
+	type Event = ();
+	type DetermineContractAddress = DummyContractAddressFor;
+	type TrieIdGenerator = DummyTrieIdGenerator;
+	type RentPayment = ();
+	type SignedClaimHandicap = pallet_contracts::DefaultSignedClaimHandicap;
+	type TombstoneDeposit = TombstoneDeposit;
+	type StorageSizeOffset = pallet_contracts::DefaultStorageSizeOffset;
+	type RentByteFee = RentByteFee;
+	type RentDepositOffset = RentDepositOffset;
+	type SurchargeReward = SurchargeReward;
+	type MaxDepth = pallet_contracts::DefaultMaxDepth;
+	type MaxValueSize = pallet_contracts::DefaultMaxValueSize;
+	type WeightPrice = ();
+}
+
+impl Trait for Test {
+	type Event = ();
+	type WeightInfo = ();
+
 }
 pub type TemplateModule = Module<Test>;
 
modifiedpallets/nft/src/tests.rsdiffbeforeafterboth
--- a/pallets/nft/src/tests.rs
+++ b/pallets/nft/src/tests.rs
@@ -292,6 +292,77 @@
         assert_eq!(TemplateModule::balance_count(1, 1), 1);
         assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
 
+        // neg transfer
+        assert_noop!(TemplateModule::transfer_from(
+            origin2.clone(),
+            1,
+            2,
+            1,
+            1,
+            1), "Only item owner, collection owner and admins can modify items");
+
+        // do approve
+        assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
+        assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1);
+        assert_eq!(
+            TemplateModule::approved(1, (1, 1))[0],
+            ApprovePermissions {
+                approved: 2,
+                amount: 100000000
+            }
+        );
+
+        assert_ok!(TemplateModule::transfer_from(
+            origin2.clone(),
+            1,
+            2,
+            1,
+            1,
+            1
+        ));
+        assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 0);
+    });
+}
+
+#[test]
+fn nft_approve_and_transfer_from_white_list() {
+    new_test_ext().execute_with(|| {
+        let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+        let mode: CollectionMode = CollectionMode::NFT(2000);
+
+        assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits { 
+            collection_numbers_limit: 10,
+            account_token_ownership_limit: 10,
+            collections_admins_limit: 5,
+            custom_data_limit: 2048,
+            nft_sponsor_transfer_timeout: 15,
+            fungible_sponsor_transfer_timeout: 15,
+            refungible_sponsor_transfer_timeout: 15,          
+        }));
+
+        let origin1 = Origin::signed(1);
+        let origin2 = Origin::signed(2);
+        assert_ok!(TemplateModule::create_collection(
+            origin1.clone(),
+            col_name1.clone(),
+            col_desc1.clone(),
+            token_prefix1.clone(),
+            mode
+        ));
+        assert_eq!(TemplateModule::collection(1).owner, 1);
+
+        assert_ok!(TemplateModule::create_item(
+            origin1.clone(),
+            1,
+            [1, 2, 3].to_vec(),
+            1
+        ));
+        assert_eq!(TemplateModule::nft_item_id(1, 1).data, [1, 2, 3].to_vec());
+        assert_eq!(TemplateModule::balance_count(1, 1), 1);
+        assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
+
         assert_ok!(TemplateModule::set_mint_permission(
             origin1.clone(),
             1,