git.delta.rocks / unique-network / refs/commits / c8be66f6b517

difftreelog

Merge pull request #14 from usetech-llc/feature/NFTPAR-109-110_contract_sponsoring

str-mv2020-11-06parents: #3f1f974 #1447327.patch.diff
in: master
Feature/nftpar 109 110 contract sponsoring

4 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,13 @@
 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'
+
 [features]
 default = ['std']
 std = [
modifiedpallets/nft/src/default_weights.rsdiffbeforeafterboth
--- a/pallets/nft/src/default_weights.rs
+++ b/pallets/nft/src/default_weights.rs
@@ -92,4 +92,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,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    // custom data size57    NFT(u32),58    // decimal points59    Fungible(u32),60    // custom data size and decimal points61    ReFungible(u32, u32),62}6364impl Into<u8> for CollectionMode {65    fn into(self) -> u8 {66        match self {67            CollectionMode::Invalid => 0,68            CollectionMode::NFT(_) => 1,69            CollectionMode::Fungible(_) => 2,70            CollectionMode::ReFungible(_, _) => 3,71        }72    }73}7475#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]76#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]77pub enum AccessMode {78    Normal,79    WhiteList,80}81impl Default for AccessMode {82    fn default() -> Self {83        Self::Normal84    }85}8687impl Default for CollectionMode {88    fn default() -> Self {89        Self::Invalid90    }91}9293#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]94#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]95pub struct Ownership<AccountId> {96    pub owner: AccountId,97    pub fraction: u128,98}99100#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]101#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]102pub struct CollectionType<AccountId> {103    pub owner: AccountId,104    pub mode: CollectionMode,105    pub access: AccessMode,106    pub decimal_points: u32,107    pub name: Vec<u16>,        // 64 include null escape char108    pub description: Vec<u16>, // 256 include null escape char109    pub token_prefix: Vec<u8>, // 16 include null escape char110    pub custom_data_size: u32,111    pub mint_mode: bool,112    pub offchain_schema: Vec<u8>,113    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender114    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship115}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 data: Vec<u8>,130}131132#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]133#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]134pub struct FungibleItemType<AccountId> {135    pub collection: u64,136    pub owner: AccountId,137    pub value: u128,138}139140#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]141#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]142pub struct ReFungibleItemType<AccountId> {143    pub collection: u64,144    pub owner: Vec<Ownership<AccountId>>,145    pub data: Vec<u8>,146}147148#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]149#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]150pub struct ApprovePermissions<AccountId> {151    pub approved: AccountId,152    pub amount: u64,153}154155#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]156#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]157pub struct VestingItem<AccountId, Moment> {158    pub sender: AccountId,159    pub recipient: AccountId,160    pub collection_id: u64,161    pub item_id: u64,162    pub amount: u64,163    pub vesting_date: Moment,164}165166#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]167#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]168pub struct BasketItem<AccountId, BlockNumber> {169    pub address: AccountId,170    pub start_block: BlockNumber,171}172173#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]174#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]175pub struct ChainLimits {176    pub collection_numbers_limit: u64,177    pub account_token_ownership_limit: u64,178    pub collections_admins_limit: u64,179    pub custom_data_limit: u32,180181    // Timeouts for item types in passed blocks182    pub nft_sponsor_transfer_timeout: u32,183    pub fungible_sponsor_transfer_timeout: u32,184    pub refungible_sponsor_transfer_timeout: u32,185}186187pub trait WeightInfo {188	fn create_collection() -> Weight;189	fn destroy_collection() -> Weight;190	fn add_to_white_list() -> Weight;191	fn remove_from_white_list() -> Weight;192    fn set_public_access_mode() -> Weight;193    fn set_mint_permission() -> Weight;194    fn change_collection_owner() -> Weight;195    fn add_collection_admin() -> Weight;196    fn remove_collection_admin() -> Weight;197    fn set_collection_sponsor() -> Weight;198    fn confirm_sponsorship() -> Weight;199    fn remove_collection_sponsor() -> Weight;200    fn create_item(s: usize, ) -> Weight;201    fn burn_item() -> Weight;202    fn transfer() -> Weight;203    fn approve() -> Weight;204    fn transfer_from() -> Weight;205    fn set_offchain_schema() -> Weight;206}207208pub trait Trait: system::Trait + Sized  {209    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;210211    /// Weight information for extrinsics in this pallet.212	type WeightInfo: WeightInfo;213}214215#[cfg(feature = "runtime-benchmarks")]216mod benchmarking;217218// #endregion219220decl_storage! {221    trait Store for Module<T: Trait> as Nft {222223        // Private members224        NextCollectionID: u64;225        CreatedCollectionCount: u64;226        ChainVersion: u64;227        ItemListIndex: map hasher(blake2_128_concat) u64 => u64;228229        // Chain limits struct230        pub ChainLimit get(fn chain_limit) config(): ChainLimits;231232        // Bound counters233        CollectionCount: u64;234        pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;235236        // Basic collections237        pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;238        pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;239        pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;240241        /// Balance owner per collection map242        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;243244        /// second parameter: item id + owner account id245        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;246247        /// Item collections248        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;249        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;250        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;251252        /// Index list253        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;254255        /// Tokens transfer baskets256        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;257        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>>;258        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;259260        // Sponsorship261        pub ContractSponsor get(fn contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;262        pub UnconfirmedContractSponsor get(fn unconfirmed_contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;263    }264    add_extra_genesis {265        build(|config: &GenesisConfig<T>| {266            // Modification of storage267            for (_num, _c) in &config.collection {268                <Module<T>>::init_collection(_c);269            }270271            for (_num, _q, _i) in &config.nft_item_id {272                <Module<T>>::init_nft_token(_i);273            }274275            for (_num, _q, _i) in &config.fungible_item_id {276                <Module<T>>::init_fungible_token(_i);277            }278279            for (_num, _q, _i) in &config.refungible_item_id {280                <Module<T>>::init_refungible_token(_i);281            }282        })283    }284}285286decl_event!(287    pub enum Event<T>288    where289        AccountId = <T as system::Trait>::AccountId,290    {291        /// New collection was created292        /// 293        /// # Arguments294        /// 295        /// * collection_id: Globally unique identifier of newly created collection.296        /// 297        /// * mode: [CollectionMode] converted into u8.298        /// 299        /// * account_id: Collection owner.300        Created(u64, u8, AccountId),301302        /// New item was created.303        /// 304        /// # Arguments305        /// 306        /// * collection_id: Id of the collection where item was created.307        /// 308        /// * item_id: Id of an item. Unique within the collection.309        ItemCreated(u64, u64),310311        /// Collection item was burned.312        /// 313        /// # Arguments314        /// 315        /// collection_id.316        /// 317        /// item_id: Identifier of burned NFT.318        ItemDestroyed(u64, u64),319    }320);321322decl_module! {323    pub struct Module<T: Trait> for enum Call where origin: T::Origin {324325        fn deposit_event() = default;326327        fn on_initialize(now: T::BlockNumber) -> Weight {328329            if ChainVersion::get() < 2330            {331                let value = NextCollectionID::get();332                CreatedCollectionCount::put(value);333                ChainVersion::put(2);334            }335336            0337        }338339        /// 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.340        /// 341        /// # Permissions342        /// 343        /// * Anyone.344        /// 345        /// # Arguments346        /// 347        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.348        /// 349        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.350        /// 351        /// * token_prefix: UTF-8 string with token prefix.352        /// 353        /// * mode: [CollectionMode] collection type and type dependent data.354        // returns collection ID355        #[weight = T::WeightInfo::create_collection()]356        pub fn create_collection(origin,357                                 collection_name: Vec<u16>,358                                 collection_description: Vec<u16>,359                                 token_prefix: Vec<u8>,360                                 mode: CollectionMode) -> DispatchResult {361362            // Anyone can create a collection363            let who = ensure_signed(origin)?;364            let custom_data_size = match mode {365                CollectionMode::NFT(size) => {366367                    // bound Custom data size368                    ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");369                    size370                },371                CollectionMode::ReFungible(size, _) => {372373                    // bound Custom data size374                    ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");375                    size376                },377                _ => 0378            };379380            let decimal_points = match mode {381                CollectionMode::Fungible(points) => points,382                CollectionMode::ReFungible(_, points) => points,383                _ => 0384            };385386            // bound Total number of collections387            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");388389            // check params390            ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");391392            let mut name = collection_name.to_vec();393            name.push(0);394            ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");395396            let mut description = collection_description.to_vec();397            description.push(0);398            ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");399400            let mut prefix = token_prefix.to_vec();401            prefix.push(0);402            ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");403404            // Generate next collection ID405            let next_id = CreatedCollectionCount::get()406                .checked_add(1)407                .expect("collection id error");408409            // bound counter410            let total = CollectionCount::get()411                .checked_add(1)412                .expect("collection counter error");413414            CreatedCollectionCount::put(next_id);415            CollectionCount::put(total);416417            // Create new collection418            let new_collection = CollectionType {419                owner: who.clone(),420                name: name,421                mode: mode.clone(),422                mint_mode: false,423                access: AccessMode::Normal,424                description: description,425                decimal_points: decimal_points,426                token_prefix: prefix,427                offchain_schema: Vec::new(),428                custom_data_size: custom_data_size,429                sponsor: T::AccountId::default(),430                unconfirmed_sponsor: T::AccountId::default(),431            };432433            // Add new collection to map434            <Collection<T>>::insert(next_id, new_collection);435436            // call event437            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));438439            Ok(())440        }441442        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.443        /// 444        /// # Permissions445        /// 446        /// * Collection Owner.447        /// 448        /// # Arguments449        /// 450        /// * collection_id: collection to destroy.451        #[weight = T::WeightInfo::destroy_collection()]452        pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {453454            let sender = ensure_signed(origin)?;455            Self::check_owner_permissions(collection_id, sender)?;456457            <AddressTokens<T>>::remove_prefix(collection_id);458            <ApprovedList<T>>::remove_prefix(collection_id);459            <Balance<T>>::remove_prefix(collection_id);460            <ItemListIndex>::remove(collection_id);461            <AdminList<T>>::remove(collection_id);462            <Collection<T>>::remove(collection_id);463            <WhiteList<T>>::remove(collection_id);464465            <NftItemList<T>>::remove_prefix(collection_id);466            <FungibleItemList<T>>::remove_prefix(collection_id);467            <ReFungibleItemList<T>>::remove_prefix(collection_id);468469            <NftTransferBasket<T>>::remove_prefix(collection_id);470            <FungibleTransferBasket<T>>::remove_prefix(collection_id);471            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);472473            if CollectionCount::get() > 0474            {475                // bound couter476                let total = CollectionCount::get()477                    .checked_sub(1)478                    .expect("collection counter error");479480                CollectionCount::put(total);481            }482483            Ok(())484        }485486        /// Add an address to white list.487        /// 488        /// # Permissions489        /// 490        /// * Collection Owner491        /// * Collection Admin492        /// 493        /// # Arguments494        /// 495        /// * collection_id.496        /// 497        /// * address.498        #[weight = T::WeightInfo::add_to_white_list()]499        pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{500501            let sender = ensure_signed(origin)?;502            Self::check_owner_or_admin_permissions(collection_id, sender)?;503504            let mut white_list_collection: Vec<T::AccountId>;505            if <WhiteList<T>>::contains_key(collection_id) {506                white_list_collection = <WhiteList<T>>::get(collection_id);507                if !white_list_collection.contains(&address.clone())508                {509                    white_list_collection.push(address.clone());510                }511            }512            else {513                white_list_collection = Vec::new();514                white_list_collection.push(address.clone());515            }516517            <WhiteList<T>>::insert(collection_id, white_list_collection);518            Ok(())519        }520521        /// Remove an address from white list.522        /// 523        /// # Permissions524        /// 525        /// * Collection Owner526        /// * Collection Admin527        /// 528        /// # Arguments529        /// 530        /// * collection_id.531        /// 532        /// * address.533        #[weight = T::WeightInfo::remove_from_white_list()]534        pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{535536            let sender = ensure_signed(origin)?;537            Self::check_owner_or_admin_permissions(collection_id, sender)?;538539            if <WhiteList<T>>::contains_key(collection_id) {540                let mut white_list_collection = <WhiteList<T>>::get(collection_id);541                if white_list_collection.contains(&address.clone())542                {543                    white_list_collection.retain(|i| *i != address.clone());544                    <WhiteList<T>>::insert(collection_id, white_list_collection);545                }546            }547548            Ok(())549        }550551        /// Toggle between normal and white list access for the methods with access for `Anyone`.552        /// 553        /// # Permissions554        /// 555        /// * Collection Owner.556        /// 557        /// # Arguments558        /// 559        /// * collection_id.560        /// 561        /// * mode: [AccessMode]562        #[weight = T::WeightInfo::set_public_access_mode()]563        pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult564        {565            let sender = ensure_signed(origin)?;566567            Self::check_owner_permissions(collection_id, sender)?;568            let mut target_collection = <Collection<T>>::get(collection_id);569            target_collection.access = mode;570            <Collection<T>>::insert(collection_id, target_collection);571572            Ok(())573        }574575        /// Allows Anyone to create tokens if:576        /// * White List is enabled, and577        /// * Address is added to white list, and578        /// * This method was called with True parameter579        /// 580        /// # Permissions581        /// * Collection Owner582        ///583        /// # Arguments584        /// 585        /// * collection_id.586        /// 587        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.588        #[weight = T::WeightInfo::set_mint_permission()]589        pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult590        {591            let sender = ensure_signed(origin)?;592593            Self::check_owner_permissions(collection_id, sender)?;594            let mut target_collection = <Collection<T>>::get(collection_id);595            target_collection.mint_mode = mint_permission;596            <Collection<T>>::insert(collection_id, target_collection);597598            Ok(())599        }600601        /// Change the owner of the collection.602        /// 603        /// # Permissions604        /// 605        /// * Collection Owner.606        /// 607        /// # Arguments608        /// 609        /// * collection_id.610        /// 611        /// * new_owner.612        #[weight = T::WeightInfo::change_collection_owner()]613        pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {614615            let sender = ensure_signed(origin)?;616            Self::check_owner_permissions(collection_id, sender)?;617            let mut target_collection = <Collection<T>>::get(collection_id);618            target_collection.owner = new_owner;619            <Collection<T>>::insert(collection_id, target_collection);620621            Ok(())622        }623624        /// Adds an admin of the Collection.625        /// 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. 626        /// 627        /// # Permissions628        /// 629        /// * Collection Owner.630        /// * Collection Admin.631        /// 632        /// # Arguments633        /// 634        /// * collection_id: ID of the Collection to add admin for.635        /// 636        /// * new_admin_id: Address of new admin to add.637        #[weight = T::WeightInfo::add_collection_admin()]638        pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {639640            let sender = ensure_signed(origin)?;641            Self::check_owner_or_admin_permissions(collection_id, sender)?;642            let mut admin_arr: Vec<T::AccountId> = Vec::new();643644            if <AdminList<T>>::contains_key(collection_id)645            {646                admin_arr = <AdminList<T>>::get(collection_id);647                ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");648            }649650            // Number of collection admins651            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");652653            admin_arr.push(new_admin_id);654            <AdminList<T>>::insert(collection_id, admin_arr);655656            Ok(())657        }658659        /// 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.660        ///661        /// # Permissions662        /// 663        /// * Collection Owner.664        /// * Collection Admin.665        /// 666        /// # Arguments667        /// 668        /// * collection_id: ID of the Collection to remove admin for.669        /// 670        /// * account_id: Address of admin to remove.671        #[weight = T::WeightInfo::remove_collection_admin()]672        pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {673674            let sender = ensure_signed(origin)?;675            Self::check_owner_or_admin_permissions(collection_id, sender)?;676677            if <AdminList<T>>::contains_key(collection_id)678            {679                let mut admin_arr = <AdminList<T>>::get(collection_id);680                admin_arr.retain(|i| *i != account_id);681                <AdminList<T>>::insert(collection_id, admin_arr);682            }683684            Ok(())685        }686687        /// # Permissions688        /// 689        /// * Collection Owner690        /// 691        /// # Arguments692        /// 693        /// * collection_id.694        /// 695        /// * new_sponsor.696        #[weight = T::WeightInfo::set_collection_sponsor()]697        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {698699            let sender = ensure_signed(origin)?;700            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");701702            let mut target_collection = <Collection<T>>::get(collection_id);703            ensure!(sender == target_collection.owner, "You do not own this collection");704705            target_collection.unconfirmed_sponsor = new_sponsor;706            <Collection<T>>::insert(collection_id, target_collection);707708            Ok(())709        }710711        /// # Permissions712        /// 713        /// * Sponsor.714        /// 715        /// # Arguments716        /// 717        /// * collection_id.718        #[weight = T::WeightInfo::confirm_sponsorship()]719        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {720721            let sender = ensure_signed(origin)?;722            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");723724            let mut target_collection = <Collection<T>>::get(collection_id);725            ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");726727            target_collection.sponsor = target_collection.unconfirmed_sponsor;728            target_collection.unconfirmed_sponsor = T::AccountId::default();729            <Collection<T>>::insert(collection_id, target_collection);730731            Ok(())732        }733734        /// Switch back to pay-per-own-transaction model.735        ///736        /// # Permissions737        ///738        /// * Collection owner.739        /// 740        /// # Arguments741        /// 742        /// * collection_id.743        #[weight = T::WeightInfo::remove_collection_sponsor()]744        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {745746            let sender = ensure_signed(origin)?;747            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");748749            let mut target_collection = <Collection<T>>::get(collection_id);750            ensure!(sender == target_collection.owner, "You do not own this collection");751752            target_collection.sponsor = T::AccountId::default();753            <Collection<T>>::insert(collection_id, target_collection);754755            Ok(())756        }757758        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.759        /// 760        /// # Permissions761        /// 762        /// * Collection Owner.763        /// * Collection Admin.764        /// * Anyone if765        ///     * White List is enabled, and766        ///     * Address is added to white list, and767        ///     * MintPermission is enabled (see SetMintPermission method)768        /// 769        /// # Arguments770        /// 771        /// * collection_id: ID of the collection.772        /// 773        /// * properties: Array of bytes that contains NFT properties. Since NFT Module is agnostic of properties meaning, it is treated purely as an array of bytes.774        /// 775        /// * owner: Address, initial owner of the NFT.776        // #[weight =777        // (130_000_000 as Weight)778        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))779        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))780        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]781782        #[weight = T::WeightInfo::create_item(properties.len())]783        pub fn create_item(origin, collection_id: u64, properties: Vec<u8>, owner: T::AccountId) -> DispatchResult {784785            let sender = ensure_signed(origin)?;786            Self::collection_exists(collection_id)?;787            let target_collection = <Collection<T>>::get(collection_id);788789            if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {790                ensure!(target_collection.mint_mode == true, "Public minting is not allowed for this collection");791                Self::check_white_list(collection_id, &owner)?;792                Self::check_white_list(collection_id, &sender)?;793            }794795            match target_collection.mode796            {797                CollectionMode::NFT(_) => {798799                    // check size800                    ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");801802                    // Create nft item803                    let item = NftItemType {804                        collection: collection_id,805                        owner: owner,806                        data: properties.clone(),807                    };808809                    Self::add_nft_item(item)?;810811                },812                CollectionMode::Fungible(_) => {813814                    // check size815                    ensure!(properties.len() as u32 == 0, "Size of item must be 0 with fungible type");816817                    let item = FungibleItemType {818                        collection: collection_id,819                        owner: owner,820                        value: (10 as u128).pow(target_collection.decimal_points)821                    };822823                    Self::add_fungible_item(item)?;824                },825                CollectionMode::ReFungible(_, _) => {826827                    // check size828                    ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");829830                    let mut owner_list = Vec::new();831                    let value = (10 as u128).pow(target_collection.decimal_points);832                    owner_list.push(Ownership {owner: owner.clone(), fraction: value});833834                    let item = ReFungibleItemType {835                        collection: collection_id,836                        owner: owner_list,837                        data: properties.clone()838                    };839840                    Self::add_refungible_item(item)?;841                },842                _ => { ensure!(1 == 0,"just error"); }843844            };845846            // call event847            Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));848849            Ok(())850        }851852        /// Destroys a concrete instance of NFT.853        /// 854        /// # Permissions855        /// 856        /// * Collection Owner.857        /// * Collection Admin.858        /// * Current NFT Owner.859        /// 860        /// # Arguments861        /// 862        /// * collection_id: ID of the collection.863        /// 864        /// * item_id: ID of NFT to burn.865        #[weight = T::WeightInfo::burn_item()]866        pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {867868            let sender = ensure_signed(origin)?;869            Self::collection_exists(collection_id)?;870871            // Transfer permissions check872            let target_collection = <Collection<T>>::get(collection_id);873            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||874                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),875                "Only item owner, collection owner and admins can modify item");876877            if target_collection.access == AccessMode::WhiteList {878                Self::check_white_list(collection_id, &sender)?;879            }880881            match target_collection.mode882            {883                CollectionMode::NFT(_) => Self::burn_nft_item(collection_id, item_id)?,884                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,885                CollectionMode::ReFungible(_, _)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,886                _ => ()887            };888889            // call event890            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));891892            Ok(())893        }894895        /// Change ownership of the token.896        /// 897        /// # Permissions898        /// 899        /// * Collection Owner900        /// * Collection Admin901        /// * Current NFT owner902        ///903        /// # Arguments904        /// 905        /// * recipient: Address of token recipient.906        /// 907        /// * collection_id.908        /// 909        /// * item_id: ID of the item910        ///     * Non-Fungible Mode: Required.911        ///     * Fungible Mode: Ignored.912        ///     * Re-Fungible Mode: Required.913        /// 914        /// * value: Amount to transfer.915        ///     * Non-Fungible Mode: Ignored916        ///     * Fungible Mode: Must specify transferred amount917        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)918        #[weight = T::WeightInfo::transfer()]919        pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {920921            let sender = ensure_signed(origin)?;922923            // Transfer permissions check924            let target_collection = <Collection<T>>::get(collection_id);925            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||926                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),927                "Only item owner, collection owner and admins can modify item");928929            if target_collection.access == AccessMode::WhiteList {930                Self::check_white_list(collection_id, &sender)?;931                Self::check_white_list(collection_id, &recipient)?;932            }933934            match target_collection.mode935            {936                CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,937                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,938                CollectionMode::ReFungible(_, _)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,939                _ => ()940            };941942            Ok(())943        }944945        /// Set, change, or remove approved address to transfer the ownership of the NFT.946        /// 947        /// # Permissions948        /// 949        /// * Collection Owner950        /// * Collection Admin951        /// * Current NFT owner952        /// 953        /// # Arguments954        /// 955        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).956        /// 957        /// * collection_id.958        /// 959        /// * item_id: ID of the item.960        #[weight = T::WeightInfo::approve()]961        pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: 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 approve");970971            if target_collection.access == AccessMode::WhiteList {972                Self::check_white_list(collection_id, &sender)?;973                Self::check_white_list(collection_id, &approved)?;974            }975976            // amount param stub977            let amount = 100000000;978979            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));980            if list_exists {981982                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));983                let item_contains = list.iter().any(|i| i.approved == approved);984985                if !item_contains {986                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });987                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);988                }989            } else {990991                let mut list = Vec::new();992                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });993                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);994            }995996            Ok(())997        }998        999        /// 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.1000        /// 1001        /// # Permissions1002        /// * Collection Owner1003        /// * Collection Admin1004        /// * Current NFT owner1005        /// * Address approved by current NFT owner1006        /// 1007        /// # Arguments1008        /// 1009        /// * from: Address that owns token.1010        /// 1011        /// * recipient: Address of token recipient.1012        /// 1013        /// * collection_id.1014        /// 1015        /// * item_id: ID of the item.1016        /// 1017        /// * value: Amount to transfer.1018        #[weight = T::WeightInfo::transfer_from()]1019        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10201021            let sender = ensure_signed(origin)?;1022            let mut appoved_transfer = false;10231024            // Check approve1025            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1026                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1027                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1028                if opt_item.is_some()1029                {1030                    appoved_transfer = true;1031                    ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1032                }1033            }10341035            // Transfer permissions check1036            let target_collection = <Collection<T>>::get(collection_id);1037            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1038                "Only item owner, collection owner and admins can modify items");10391040            if target_collection.access == AccessMode::WhiteList {1041                Self::check_white_list(collection_id, &sender)?;1042                Self::check_white_list(collection_id, &recipient)?;1043            }10441045            // remove approve1046            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1047                .into_iter().filter(|i| i.approved != sender.clone()).collect();1048            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);104910501051            match target_collection.mode1052            {1053                CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, from, recipient)?,1054                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1055                CollectionMode::ReFungible(_, _)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1056                _ => ()1057            };10581059            Ok(())1060        }10611062        ///1063        #[weight = 0]1064        pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {10651066            // let no_perm_mes = "You do not have permissions to modify this collection";1067            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1068            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1069            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);10701071            // // on_nft_received  call10721073            // Self::transfer(origin, collection_id, item_id, new_owner)?;10741075            Ok(())1076        }10771078        /// Set off-chain data schema.1079        /// 1080        /// # Permissions1081        /// 1082        /// * Collection Owner1083        /// * Collection Admin1084        /// 1085        /// # Arguments1086        /// 1087        /// * collection_id.1088        /// 1089        /// * schema: String representing the offchain data schema.1090        #[weight = T::WeightInfo::set_offchain_schema()]1091        pub fn set_offchain_schema(1092            origin,1093            collection_id: u64,1094            schema: Vec<u8>1095        ) -> DispatchResult {1096            let sender = ensure_signed(origin)?;1097            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;10981099            let mut target_collection = <Collection<T>>::get(collection_id);1100            target_collection.offchain_schema = schema;1101            <Collection<T>>::insert(collection_id, target_collection);11021103            Ok(())1104        }11051106        // Sudo permissions function1107        #[weight = 0]1108        pub fn set_chain_limits(1109            origin,1110            limits: ChainLimits1111        ) -> DispatchResult {1112            ensure_root(origin)?;1113            <ChainLimit>::put(limits);1114            Ok(())1115        }        1116    }1117}11181119impl<T: Trait> Module<T> {1120    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1121        let current_index = <ItemListIndex>::get(item.collection)1122            .checked_add(1)1123            .expect("Item list index id error");1124        let itemcopy = item.clone();1125        let owner = item.owner.clone();1126        let value = item.value as u64;11271128        Self::add_token_index(item.collection, current_index, owner.clone())?;11291130        <ItemListIndex>::insert(item.collection, current_index);1131        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);11321133        // Add current block1134        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1135        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1136        1137        // Update balance1138        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1139            .checked_add(value)1140            .unwrap();1141        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);11421143        Ok(())1144    }11451146    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1147        let current_index = <ItemListIndex>::get(item.collection)1148            .checked_add(1)1149            .expect("Item list index id error");1150        let itemcopy = item.clone();11511152        let value = item.owner.first().unwrap().fraction as u64;1153        let owner = item.owner.first().unwrap().owner.clone();11541155        Self::add_token_index(item.collection, current_index, owner.clone())?;11561157        <ItemListIndex>::insert(item.collection, current_index);1158        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);11591160        // Add current block1161        let block_number: T::BlockNumber = 0.into();1162        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);11631164        // Update balance1165        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1166            .checked_add(value)1167            .unwrap();1168        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);11691170        Ok(())1171    }11721173    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1174        let current_index = <ItemListIndex>::get(item.collection)1175            .checked_add(1)1176            .expect("Item list index id error");11771178        let item_owner = item.owner.clone();1179        let collection_id = item.collection.clone();1180        Self::add_token_index(collection_id, current_index, item.owner.clone())?;11811182        <ItemListIndex>::insert(collection_id, current_index);1183        <NftItemList<T>>::insert(collection_id, current_index, item);11841185        // Add current block1186        let block_number: T::BlockNumber = 0.into();1187        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);11881189        // Update balance1190        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1191            .checked_add(1)1192            .unwrap();1193        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);11941195        Ok(())1196    }11971198    fn burn_refungible_item(1199        collection_id: u64,1200        item_id: u64,1201        owner: T::AccountId,1202    ) -> DispatchResult {1203        ensure!(1204            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1205            "Item does not exists"1206        );1207        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1208        let item = collection1209            .owner1210            .iter()1211            .filter(|&i| i.owner == owner)1212            .next()1213            .unwrap();1214        Self::remove_token_index(collection_id, item_id, owner.clone())?;12151216        // remove approve list1217        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));12181219        // update balance1220        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1221            .checked_sub(item.fraction as u64)1222            .unwrap();1223        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);12241225        <ReFungibleItemList<T>>::remove(collection_id, item_id);12261227        Ok(())1228    }12291230    fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1231        ensure!(1232            <NftItemList<T>>::contains_key(collection_id, item_id),1233            "Item does not exists"1234        );1235        let item = <NftItemList<T>>::get(collection_id, item_id);1236        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;12371238        // remove approve list1239        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));12401241        // update balance1242        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1243            .checked_sub(1)1244            .unwrap();1245        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1246        <NftItemList<T>>::remove(collection_id, item_id);12471248        Ok(())1249    }12501251    fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1252        ensure!(1253            <FungibleItemList<T>>::contains_key(collection_id, item_id),1254            "Item does not exists"1255        );1256        let item = <FungibleItemList<T>>::get(collection_id, item_id);1257        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;12581259        // remove approve list1260        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));12611262        // update balance1263        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1264            .checked_sub(item.value as u64)1265            .unwrap();1266        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);12671268        <FungibleItemList<T>>::remove(collection_id, item_id);12691270        Ok(())1271    }12721273    fn collection_exists(collection_id: u64) -> DispatchResult {1274        ensure!(1275            <Collection<T>>::contains_key(collection_id),1276            "This collection does not exist"1277        );1278        Ok(())1279    }12801281    fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1282        Self::collection_exists(collection_id)?;12831284        let target_collection = <Collection<T>>::get(collection_id);1285        ensure!(1286            subject == target_collection.owner,1287            "You do not own this collection"1288        );12891290        Ok(())1291    }12921293    fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1294        let target_collection = <Collection<T>>::get(collection_id);1295        let mut result: bool = subject == target_collection.owner;1296        let exists = <AdminList<T>>::contains_key(collection_id);12971298        if !result & exists {1299            if <AdminList<T>>::get(collection_id).contains(&subject) {1300                result = true1301            }1302        }13031304        result1305    }13061307    fn check_owner_or_admin_permissions(1308        collection_id: u64,1309        subject: T::AccountId,1310    ) -> DispatchResult {1311        Self::collection_exists(collection_id)?;1312        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());13131314        ensure!(1315            result,1316            "You do not have permissions to modify this collection"1317        );1318        Ok(())1319    }13201321    fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1322        let target_collection = <Collection<T>>::get(collection_id);13231324        match target_collection.mode {1325            CollectionMode::NFT(_) => {1326                <NftItemList<T>>::get(collection_id, item_id).owner == subject1327            }1328            CollectionMode::Fungible(_) => {1329                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1330            }1331            CollectionMode::ReFungible(_, _) => {1332                <ReFungibleItemList<T>>::get(collection_id, item_id)1333                    .owner1334                    .iter()1335                    .any(|i| i.owner == subject)1336            }1337            CollectionMode::Invalid => false,1338        }1339    }13401341    fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1342        let mes = "Address is not in white list";1343        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1344        let wl = <WhiteList<T>>::get(collection_id);1345        ensure!(wl.contains(address), mes);13461347        Ok(())1348    }13491350    fn transfer_fungible(1351        collection_id: u64,1352        item_id: u64,1353        value: u64,1354        owner: T::AccountId,1355        new_owner: T::AccountId,1356    ) -> DispatchResult {1357        ensure!(1358            <FungibleItemList<T>>::contains_key(collection_id, item_id),1359            "Item not exists"1360        );13611362        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1363        let amount = full_item.value;13641365        ensure!(amount >= value.into(), "Item balance not enouth");13661367        // update balance1368        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1369            .checked_sub(value)1370            .unwrap();1371        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);13721373        let mut new_owner_account_id = 0;1374        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1375        if new_owner_items.len() > 0 {1376            new_owner_account_id = new_owner_items[0];1377        }13781379        let val64 = value.into();13801381        // transfer1382        if amount == val64 && new_owner_account_id == 0 {1383            // change owner1384            // new owner do not have account1385            let mut new_full_item = full_item.clone();1386            new_full_item.owner = new_owner.clone();1387            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);13881389            // update balance1390            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1391                .checked_add(value)1392                .unwrap();1393            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);13941395            // update index collection1396            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1397        } else {1398            let mut new_full_item = full_item.clone();1399            new_full_item.value -= val64;14001401            // separate amount1402            if new_owner_account_id > 0 {1403                // new owner has account1404                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1405                item.value += val64;14061407                // update balance1408                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1409                    .checked_add(value)1410                    .unwrap();1411                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14121413                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1414            } else {1415                // new owner do not have account1416                let item = FungibleItemType {1417                    collection: collection_id,1418                    owner: new_owner.clone(),1419                    value: val64,1420                };14211422                Self::add_fungible_item(item)?;1423            }14241425            if amount == val64 {1426                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;14271428                // remove approve list1429                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1430                <FungibleItemList<T>>::remove(collection_id, item_id);1431            }14321433            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1434        }14351436        Ok(())1437    }14381439    fn transfer_refungible(1440        collection_id: u64,1441        item_id: u64,1442        value: u64,1443        owner: T::AccountId,1444        new_owner: T::AccountId,1445    ) -> DispatchResult {1446        ensure!(1447            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1448            "Item not exists"1449        );14501451        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1452        let item = full_item1453            .owner1454            .iter()1455            .filter(|i| i.owner == owner)1456            .next()1457            .unwrap();1458        let amount = item.fraction;14591460        ensure!(amount >= value.into(), "Item balance not enouth");14611462        // update balance1463        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1464            .checked_sub(value)1465            .unwrap();1466        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);14671468        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1469            .checked_add(value)1470            .unwrap();1471        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14721473        let old_owner = item.owner.clone();1474        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1475        let val64 = value.into();14761477        // transfer1478        if amount == val64 && !new_owner_has_account {1479            // change owner1480            // new owner do not have account1481            let mut new_full_item = full_item.clone();1482            new_full_item1483                .owner1484                .iter_mut()1485                .find(|i| i.owner == owner)1486                .unwrap()1487                .owner = new_owner.clone();1488            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);14891490            // update index collection1491            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1492        } else {1493            let mut new_full_item = full_item.clone();1494            new_full_item1495                .owner1496                .iter_mut()1497                .find(|i| i.owner == owner)1498                .unwrap()1499                .fraction -= val64;15001501            // separate amount1502            if new_owner_has_account {1503                // new owner has account1504                new_full_item1505                    .owner1506                    .iter_mut()1507                    .find(|i| i.owner == new_owner)1508                    .unwrap()1509                    .fraction += val64;1510            } else {1511                // new owner do not have account1512                new_full_item.owner.push(Ownership {1513                    owner: new_owner.clone(),1514                    fraction: val64,1515                });1516                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1517            }15181519            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1520        }15211522        Ok(())1523    }15241525    fn transfer_nft(1526        collection_id: u64,1527        item_id: u64,1528        sender: T::AccountId,1529        new_owner: T::AccountId,1530    ) -> DispatchResult {1531        ensure!(1532            <NftItemList<T>>::contains_key(collection_id, item_id),1533            "Item not exists"1534        );15351536        let mut item = <NftItemList<T>>::get(collection_id, item_id);15371538        ensure!(1539            sender == item.owner,1540            "sender parameter and item owner must be equal"1541        );15421543        // update balance1544        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1545            .checked_sub(1)1546            .unwrap();1547        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);15481549        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1550            .checked_add(1)1551            .unwrap();1552        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15531554        // change owner1555        let old_owner = item.owner.clone();1556        item.owner = new_owner.clone();1557        <NftItemList<T>>::insert(collection_id, item_id, item);15581559        // update index collection1560        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;15611562        // reset approved list1563        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1564        Ok(())1565    }15661567    fn init_collection(item: &CollectionType<T::AccountId>) {1568        // check params1569        assert!(1570            item.decimal_points <= 4,1571            "decimal_points parameter must be lower than 4"1572        );1573        assert!(1574            item.name.len() <= 64,1575            "Collection name can not be longer than 63 char"1576        );1577        assert!(1578            item.name.len() <= 256,1579            "Collection description can not be longer than 255 char"1580        );1581        assert!(1582            item.token_prefix.len() <= 16,1583            "Token prefix can not be longer than 15 char"1584        );15851586        // Generate next collection ID1587        let next_id = CreatedCollectionCount::get()1588            .checked_add(1)1589            .expect("collection id error");15901591        CreatedCollectionCount::put(next_id);1592    }15931594    fn init_nft_token(item: &NftItemType<T::AccountId>) {1595        let current_index = <ItemListIndex>::get(item.collection)1596            .checked_add(1)1597            .expect("Item list index id error");15981599        let item_owner = item.owner.clone();1600        let collection_id = item.collection.clone();1601        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();16021603        <ItemListIndex>::insert(collection_id, current_index);16041605        // Update balance1606        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1607            .checked_add(1)1608            .unwrap();1609        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1610    }16111612    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1613        let current_index = <ItemListIndex>::get(item.collection)1614            .checked_add(1)1615            .expect("Item list index id error");1616        let owner = item.owner.clone();1617        let value = item.value as u64;16181619        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();16201621        <ItemListIndex>::insert(item.collection, current_index);16221623        // Update balance1624        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1625            .checked_add(value)1626            .unwrap();1627        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1628    }16291630    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1631        let current_index = <ItemListIndex>::get(item.collection)1632            .checked_add(1)1633            .expect("Item list index id error");16341635        let value = item.owner.first().unwrap().fraction as u64;1636        let owner = item.owner.first().unwrap().owner.clone();16371638        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();16391640        <ItemListIndex>::insert(item.collection, current_index);16411642        // Update balance1643        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1644            .checked_add(value)1645            .unwrap();1646        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1647    }16481649    fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {16501651        // add to account limit1652        if <AccountItemCount<T>>::contains_key(owner.clone()) {16531654            // bound Owned tokens by a single address1655            let count = <AccountItemCount<T>>::get(owner.clone());1656            ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");16571658            <AccountItemCount<T>>::insert(owner.clone(), 1659                count.checked_add(1).unwrap());1660        }1661        else {1662            <AccountItemCount<T>>::insert(owner.clone(), 1);1663        }16641665        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1666        if list_exists {1667            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1668            let item_contains = list.contains(&item_index.clone());16691670            if !item_contains {1671                list.push(item_index.clone());1672            }16731674            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1675        } else {1676            let mut itm = Vec::new();1677            itm.push(item_index.clone());1678            <AddressTokens<T>>::insert(collection_id, owner, itm);1679            1680        }16811682        Ok(())1683    }16841685    fn remove_token_index(1686        collection_id: u64,1687        item_index: u64,1688        owner: T::AccountId,1689    ) -> DispatchResult {16901691        // update counter1692        <AccountItemCount<T>>::insert(owner.clone(), 1693            <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());169416951696        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1697        if list_exists {1698            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1699            let item_contains = list.contains(&item_index.clone());17001701            if item_contains {1702                list.retain(|&item| item != item_index);1703                <AddressTokens<T>>::insert(collection_id, owner, list);1704            }1705        }17061707        Ok(())1708    }17091710    fn move_token_index(1711        collection_id: u64,1712        item_index: u64,1713        old_owner: T::AccountId,1714        new_owner: T::AccountId,1715    ) -> DispatchResult {1716        Self::remove_token_index(collection_id, item_index, old_owner)?;1717        Self::add_token_index(collection_id, item_index, new_owner)?;17181719        Ok(())1720    }1721}17221723////////////////////////////////////////////////////////////////////////////////////////////////////1724// Economic models1725// #region17261727/// Fee multiplier.1728pub type Multiplier = FixedU128;17291730type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1731    <T as system::Trait>::AccountId,1732>>::Balance;1733type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1734    <T as system::Trait>::AccountId,1735>>::NegativeImbalance;17361737/// Require the transactor pay for themselves and maybe include a tip to gain additional priority1738/// in the queue.1739#[derive(Encode, Decode, Clone, Eq, PartialEq)]1740pub struct ChargeTransactionPayment<T: transaction_payment::Trait + Send + Sync>(1741    #[codec(compact)] BalanceOf<T>,1742);17431744impl<T: Trait + transaction_payment::Trait + Send + Sync> sp_std::fmt::Debug1745    for ChargeTransactionPayment<T>1746{1747    #[cfg(feature = "std")]1748    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1749        write!(f, "ChargeTransactionPayment<{:?}>", self.0)1750    }1751    #[cfg(not(feature = "std"))]1752    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1753        Ok(())1754    }1755}17561757impl<T: Trait + transaction_payment::Trait + Send + Sync> ChargeTransactionPayment<T>1758where1759    T::Call:1760        Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>>,1761    BalanceOf<T>: Send + Sync + FixedPointOperand,1762{1763    /// utility constructor. Used only in client/factory code.1764    pub fn from(fee: BalanceOf<T>) -> Self {1765        Self(fee)1766    }17671768    pub fn traditional_fee(1769        len: usize,1770        info: &DispatchInfoOf<T::Call>,1771        tip: BalanceOf<T>,1772    ) -> BalanceOf<T>1773    where1774        T::Call: Dispatchable<Info = DispatchInfo>,1775    {1776        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)1777    }17781779    fn withdraw_fee(1780        &self,1781        who: &T::AccountId,1782        call: &T::Call,1783        info: &DispatchInfoOf<T::Call>,1784        len: usize,1785    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {1786        let tip = self.0;17871788        // Set fee based on call type. Creating collection costs 1 Unique.1789        // All other transactions have traditional fees so far1790        let fee = match call.is_sub_type() {1791            Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),1792            _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes1793                                                        // _ => <BalanceOf<T>>::from(100)1794        };17951796        // Determine who is paying transaction fee based on ecnomic model1797        // Parse call to extract collection ID and access collection sponsor1798        let sponsor: T::AccountId = match call.is_sub_type() {1799            Some(Call::create_item(collection_id, _properties, _owner)) => {1800                <Collection<T>>::get(collection_id).sponsor1801            }1802            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {1803                let _collection_mode = <Collection<T>>::get(collection_id).mode;18041805                // sponsor timeout1806                let sponsor_transfer = match _collection_mode {1807                    CollectionMode::NFT(_) => {1808                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);1809                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1810                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1811                        if block_number >= limit_time {1812                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);1813                            true1814                        }1815                        else {1816                            false1817                        }1818                    }1819                    CollectionMode::Fungible(_) => {1820                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);1821                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1822                        if basket.iter().any(|i| i.address == _new_owner.clone())1823                        {1824                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();1825                            let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();1826                            if block_number >= limit_time {1827                                basket.retain(|x| x.address == item.address);1828                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });1829                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);1830                                true1831                            }1832                            else {1833                                false1834                            }1835                        }1836                        else {1837                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});1838                            true1839                        }1840                    }1841                    CollectionMode::ReFungible(_, _) => {1842                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);1843                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1844                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1845                        if block_number >= limit_time {1846                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);1847                            true1848                        } else {1849                            false1850                        }1851                    }1852                    _ => {1853                        false1854                    },1855                };18561857                if !sponsor_transfer {1858                    T::AccountId::default()1859                } else {1860                    <Collection<T>>::get(collection_id).sponsor1861                }1862            }18631864            _ => T::AccountId::default(),1865        };18661867        let mut who_pays_fee: T::AccountId = sponsor.clone();1868        if sponsor == T::AccountId::default() {1869            who_pays_fee = who.clone();1870        }18711872        // Only mess with balances if fee is not zero.1873        if fee.is_zero() {1874            return Ok((fee, None));1875        }18761877        match <T as transaction_payment::Trait>::Currency::withdraw(1878            &who_pays_fee,1879            fee,1880            if tip.is_zero() {1881                WithdrawReason::TransactionPayment.into()1882            } else {1883                WithdrawReason::TransactionPayment | WithdrawReason::Tip1884            },1885            ExistenceRequirement::KeepAlive,1886        ) {1887            Ok(imbalance) => Ok((fee, Some(imbalance))),1888            Err(_) => Err(InvalidTransaction::Payment.into()),1889        }1890    }1891}18921893impl<T: Trait + transaction_payment::Trait + Send + Sync> SignedExtension1894    for ChargeTransactionPayment<T>1895where1896    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,1897    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>>,1898{1899    const IDENTIFIER: &'static str = "ChargeTransactionPayment";1900    type AccountId = T::AccountId;1901    type Call = T::Call;1902    type AdditionalSigned = ();1903    type Pre = (1904        BalanceOf<T>,1905        Self::AccountId,1906        Option<NegativeImbalanceOf<T>>,1907        BalanceOf<T>,1908    );1909    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {1910        Ok(())1911    }19121913    fn validate(1914        &self,1915        who: &Self::AccountId,1916        call: &Self::Call,1917        info: &DispatchInfoOf<Self::Call>,1918        len: usize,1919    ) -> TransactionValidity {1920        let (fee, _) = self.withdraw_fee(who, call, info, len)?;19211922        let mut r = ValidTransaction::default();1923        // NOTE: we probably want to maximize the _fee (of any type) per weight unit_ here, which1924        // will be a bit more than setting the priority to tip. For now, this is enough.1925        r.priority = fee.saturated_into::<TransactionPriority>();1926        Ok(r)1927    }19281929    fn pre_dispatch(1930        self,1931        who: &Self::AccountId,1932        call: &Self::Call,1933        info: &DispatchInfoOf<Self::Call>,1934        len: usize,1935    ) -> Result<Self::Pre, TransactionValidityError> {1936        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;1937        Ok((self.0, who.clone(), imbalance, fee))1938    }19391940    fn post_dispatch(1941        pre: Self::Pre,1942        info: &DispatchInfoOf<Self::Call>,1943        post_info: &PostDispatchInfoOf<Self::Call>,1944        len: usize,1945        _result: &DispatchResult,1946    ) -> Result<(), TransactionValidityError> {1947        let (tip, who, imbalance, fee) = pre;1948        if let Some(payed) = imbalance {1949            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(1950                len as u32, info, post_info, tip,1951            );1952            let refund = fee.saturating_sub(actual_fee);1953            let actual_payment =1954                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(1955                    &who, refund,1956                ) {1957                    Ok(refund_imbalance) => {1958                        // The refund cannot be larger than the up front payed max weight.1959                        // `PostDispatchInfo::calc_unspent` guards against such a case.1960                        match payed.offset(refund_imbalance) {1961                            Ok(actual_payment) => actual_payment,1962                            Err(_) => return Err(InvalidTransaction::Payment.into()),1963                        }1964                    }1965                    // We do not recreate the account using the refund. The up front payment1966                    // is gone in that case.1967                    Err(_) => payed,1968                };1969            let imbalances = actual_payment.split(tip);1970            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(1971                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),1972            );1973        }1974        Ok(())1975    }1976}1977// #endregion19781979
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,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    // custom data size57    NFT(u32),58    // decimal points59    Fungible(u32),60    // custom data size and decimal points61    ReFungible(u32, u32),62}6364impl Into<u8> for CollectionMode {65    fn into(self) -> u8 {66        match self {67            CollectionMode::Invalid => 0,68            CollectionMode::NFT(_) => 1,69            CollectionMode::Fungible(_) => 2,70            CollectionMode::ReFungible(_, _) => 3,71        }72    }73}7475#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]76#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]77pub enum AccessMode {78    Normal,79    WhiteList,80}81impl Default for AccessMode {82    fn default() -> Self {83        Self::Normal84    }85}8687impl Default for CollectionMode {88    fn default() -> Self {89        Self::Invalid90    }91}9293#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]94#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]95pub struct Ownership<AccountId> {96    pub owner: AccountId,97    pub fraction: u128,98}99100#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]101#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]102pub struct CollectionType<AccountId> {103    pub owner: AccountId,104    pub mode: CollectionMode,105    pub access: AccessMode,106    pub decimal_points: u32,107    pub name: Vec<u16>,        // 64 include null escape char108    pub description: Vec<u16>, // 256 include null escape char109    pub token_prefix: Vec<u8>, // 16 include null escape char110    pub custom_data_size: u32,111    pub mint_mode: bool,112    pub offchain_schema: Vec<u8>,113    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender114    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship115}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 data: Vec<u8>,130}131132#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]133#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]134pub struct FungibleItemType<AccountId> {135    pub collection: u64,136    pub owner: AccountId,137    pub value: u128,138}139140#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]141#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]142pub struct ReFungibleItemType<AccountId> {143    pub collection: u64,144    pub owner: Vec<Ownership<AccountId>>,145    pub data: Vec<u8>,146}147148#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]149#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]150pub struct ApprovePermissions<AccountId> {151    pub approved: AccountId,152    pub amount: u64,153}154155#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]156#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]157pub struct VestingItem<AccountId, Moment> {158    pub sender: AccountId,159    pub recipient: AccountId,160    pub collection_id: u64,161    pub item_id: u64,162    pub amount: u64,163    pub vesting_date: Moment,164}165166#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]167#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]168pub struct BasketItem<AccountId, BlockNumber> {169    pub address: AccountId,170    pub start_block: BlockNumber,171}172173#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]174#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]175pub struct ChainLimits {176    pub collection_numbers_limit: u64,177    pub account_token_ownership_limit: u64,178    pub collections_admins_limit: u64,179    pub custom_data_limit: u32,180181    // Timeouts for item types in passed blocks182    pub nft_sponsor_transfer_timeout: u32,183    pub fungible_sponsor_transfer_timeout: u32,184    pub refungible_sponsor_transfer_timeout: u32,185}186187pub trait WeightInfo {188	fn create_collection() -> Weight;189	fn destroy_collection() -> Weight;190	fn add_to_white_list() -> Weight;191	fn remove_from_white_list() -> Weight;192    fn set_public_access_mode() -> Weight;193    fn set_mint_permission() -> Weight;194    fn change_collection_owner() -> Weight;195    fn add_collection_admin() -> Weight;196    fn remove_collection_admin() -> Weight;197    fn set_collection_sponsor() -> Weight;198    fn confirm_sponsorship() -> Weight;199    fn remove_collection_sponsor() -> Weight;200    fn create_item(s: usize, ) -> Weight;201    fn burn_item() -> Weight;202    fn transfer() -> Weight;203    fn approve() -> Weight;204    fn transfer_from() -> Weight;205    fn set_offchain_schema() -> Weight;206    // fn enable_contract_sponsoring() -> Weight;207}208209pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {210    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;211212    /// Weight information for extrinsics in this pallet.213	type WeightInfo: WeightInfo;214}215216#[cfg(feature = "runtime-benchmarks")]217mod benchmarking;218219// #endregion220221decl_storage! {222    trait Store for Module<T: Trait> as Nft {223224        // Private members225        NextCollectionID: u64;226        CreatedCollectionCount: u64;227        ChainVersion: u64;228        ItemListIndex: map hasher(blake2_128_concat) u64 => u64;229230        // Chain limits struct231        pub ChainLimit get(fn chain_limit) config(): ChainLimits;232233        // Bound counters234        CollectionCount: u64;235        pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;236237        // Basic collections238        pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;239        pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;240        pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;241242        /// Balance owner per collection map243        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;244245        /// second parameter: item id + owner account id246        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;247248        /// Item collections249        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;250        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;251        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;252253        /// Index list254        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;255256        /// Tokens transfer baskets257        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;258        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>>;259        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;260261        // Contract Sponsorship and Ownership262        pub ContractOwner get(fn contract_owner): map hasher(identity) T::AccountId => T::AccountId;263        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(identity) T::AccountId => bool;264    }265    add_extra_genesis {266        build(|config: &GenesisConfig<T>| {267            // Modification of storage268            for (_num, _c) in &config.collection {269                <Module<T>>::init_collection(_c);270            }271272            for (_num, _q, _i) in &config.nft_item_id {273                <Module<T>>::init_nft_token(_i);274            }275276            for (_num, _q, _i) in &config.fungible_item_id {277                <Module<T>>::init_fungible_token(_i);278            }279280            for (_num, _q, _i) in &config.refungible_item_id {281                <Module<T>>::init_refungible_token(_i);282            }283        })284    }285}286287decl_event!(288    pub enum Event<T>289    where290        AccountId = <T as system::Trait>::AccountId,291    {292        /// New collection was created293        /// 294        /// # Arguments295        /// 296        /// * collection_id: Globally unique identifier of newly created collection.297        /// 298        /// * mode: [CollectionMode] converted into u8.299        /// 300        /// * account_id: Collection owner.301        Created(u64, u8, AccountId),302303        /// New item was created.304        /// 305        /// # Arguments306        /// 307        /// * collection_id: Id of the collection where item was created.308        /// 309        /// * item_id: Id of an item. Unique within the collection.310        ItemCreated(u64, u64),311312        /// Collection item was burned.313        /// 314        /// # Arguments315        /// 316        /// collection_id.317        /// 318        /// item_id: Identifier of burned NFT.319        ItemDestroyed(u64, u64),320    }321);322323decl_module! {324    pub struct Module<T: Trait> for enum Call where origin: T::Origin {325326        fn deposit_event() = default;327328        fn on_initialize(now: T::BlockNumber) -> Weight {329330            if ChainVersion::get() < 2331            {332                let value = NextCollectionID::get();333                CreatedCollectionCount::put(value);334                ChainVersion::put(2);335            }336337            0338        }339340        /// 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.341        /// 342        /// # Permissions343        /// 344        /// * Anyone.345        /// 346        /// # Arguments347        /// 348        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.349        /// 350        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.351        /// 352        /// * token_prefix: UTF-8 string with token prefix.353        /// 354        /// * mode: [CollectionMode] collection type and type dependent data.355        // returns collection ID356        #[weight = T::WeightInfo::create_collection()]357        pub fn create_collection(origin,358                                 collection_name: Vec<u16>,359                                 collection_description: Vec<u16>,360                                 token_prefix: Vec<u8>,361                                 mode: CollectionMode) -> DispatchResult {362363            // Anyone can create a collection364            let who = ensure_signed(origin)?;365            let custom_data_size = match mode {366                CollectionMode::NFT(size) => {367368                    // bound Custom data size369                    ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");370                    size371                },372                CollectionMode::ReFungible(size, _) => {373374                    // bound Custom data size375                    ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");376                    size377                },378                _ => 0379            };380381            let decimal_points = match mode {382                CollectionMode::Fungible(points) => points,383                CollectionMode::ReFungible(_, points) => points,384                _ => 0385            };386387            // bound Total number of collections388            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");389390            // check params391            ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");392393            let mut name = collection_name.to_vec();394            name.push(0);395            ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");396397            let mut description = collection_description.to_vec();398            description.push(0);399            ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");400401            let mut prefix = token_prefix.to_vec();402            prefix.push(0);403            ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");404405            // Generate next collection ID406            let next_id = CreatedCollectionCount::get()407                .checked_add(1)408                .expect("collection id error");409410            // bound counter411            let total = CollectionCount::get()412                .checked_add(1)413                .expect("collection counter error");414415            CreatedCollectionCount::put(next_id);416            CollectionCount::put(total);417418            // Create new collection419            let new_collection = CollectionType {420                owner: who.clone(),421                name: name,422                mode: mode.clone(),423                mint_mode: false,424                access: AccessMode::Normal,425                description: description,426                decimal_points: decimal_points,427                token_prefix: prefix,428                offchain_schema: Vec::new(),429                custom_data_size: custom_data_size,430                sponsor: T::AccountId::default(),431                unconfirmed_sponsor: T::AccountId::default(),432            };433434            // Add new collection to map435            <Collection<T>>::insert(next_id, new_collection);436437            // call event438            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));439440            Ok(())441        }442443        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.444        /// 445        /// # Permissions446        /// 447        /// * Collection Owner.448        /// 449        /// # Arguments450        /// 451        /// * collection_id: collection to destroy.452        #[weight = T::WeightInfo::destroy_collection()]453        pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {454455            let sender = ensure_signed(origin)?;456            Self::check_owner_permissions(collection_id, sender)?;457458            <AddressTokens<T>>::remove_prefix(collection_id);459            <ApprovedList<T>>::remove_prefix(collection_id);460            <Balance<T>>::remove_prefix(collection_id);461            <ItemListIndex>::remove(collection_id);462            <AdminList<T>>::remove(collection_id);463            <Collection<T>>::remove(collection_id);464            <WhiteList<T>>::remove(collection_id);465466            <NftItemList<T>>::remove_prefix(collection_id);467            <FungibleItemList<T>>::remove_prefix(collection_id);468            <ReFungibleItemList<T>>::remove_prefix(collection_id);469470            <NftTransferBasket<T>>::remove_prefix(collection_id);471            <FungibleTransferBasket<T>>::remove_prefix(collection_id);472            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);473474            if CollectionCount::get() > 0475            {476                // bound couter477                let total = CollectionCount::get()478                    .checked_sub(1)479                    .expect("collection counter error");480481                CollectionCount::put(total);482            }483484            Ok(())485        }486487        /// Add an address to white list.488        /// 489        /// # Permissions490        /// 491        /// * Collection Owner492        /// * Collection Admin493        /// 494        /// # Arguments495        /// 496        /// * collection_id.497        /// 498        /// * address.499        #[weight = T::WeightInfo::add_to_white_list()]500        pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{501502            let sender = ensure_signed(origin)?;503            Self::check_owner_or_admin_permissions(collection_id, sender)?;504505            let mut white_list_collection: Vec<T::AccountId>;506            if <WhiteList<T>>::contains_key(collection_id) {507                white_list_collection = <WhiteList<T>>::get(collection_id);508                if !white_list_collection.contains(&address.clone())509                {510                    white_list_collection.push(address.clone());511                }512            }513            else {514                white_list_collection = Vec::new();515                white_list_collection.push(address.clone());516            }517518            <WhiteList<T>>::insert(collection_id, white_list_collection);519            Ok(())520        }521522        /// Remove an address from white list.523        /// 524        /// # Permissions525        /// 526        /// * Collection Owner527        /// * Collection Admin528        /// 529        /// # Arguments530        /// 531        /// * collection_id.532        /// 533        /// * address.534        #[weight = T::WeightInfo::remove_from_white_list()]535        pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{536537            let sender = ensure_signed(origin)?;538            Self::check_owner_or_admin_permissions(collection_id, sender)?;539540            if <WhiteList<T>>::contains_key(collection_id) {541                let mut white_list_collection = <WhiteList<T>>::get(collection_id);542                if white_list_collection.contains(&address.clone())543                {544                    white_list_collection.retain(|i| *i != address.clone());545                    <WhiteList<T>>::insert(collection_id, white_list_collection);546                }547            }548549            Ok(())550        }551552        /// Toggle between normal and white list access for the methods with access for `Anyone`.553        /// 554        /// # Permissions555        /// 556        /// * Collection Owner.557        /// 558        /// # Arguments559        /// 560        /// * collection_id.561        /// 562        /// * mode: [AccessMode]563        #[weight = T::WeightInfo::set_public_access_mode()]564        pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult565        {566            let sender = ensure_signed(origin)?;567568            Self::check_owner_permissions(collection_id, sender)?;569            let mut target_collection = <Collection<T>>::get(collection_id);570            target_collection.access = mode;571            <Collection<T>>::insert(collection_id, target_collection);572573            Ok(())574        }575576        /// Allows Anyone to create tokens if:577        /// * White List is enabled, and578        /// * Address is added to white list, and579        /// * This method was called with True parameter580        /// 581        /// # Permissions582        /// * Collection Owner583        ///584        /// # Arguments585        /// 586        /// * collection_id.587        /// 588        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.589        #[weight = T::WeightInfo::set_mint_permission()]590        pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult591        {592            let sender = ensure_signed(origin)?;593594            Self::check_owner_permissions(collection_id, sender)?;595            let mut target_collection = <Collection<T>>::get(collection_id);596            target_collection.mint_mode = mint_permission;597            <Collection<T>>::insert(collection_id, target_collection);598599            Ok(())600        }601602        /// Change the owner of the collection.603        /// 604        /// # Permissions605        /// 606        /// * Collection Owner.607        /// 608        /// # Arguments609        /// 610        /// * collection_id.611        /// 612        /// * new_owner.613        #[weight = T::WeightInfo::change_collection_owner()]614        pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {615616            let sender = ensure_signed(origin)?;617            Self::check_owner_permissions(collection_id, sender)?;618            let mut target_collection = <Collection<T>>::get(collection_id);619            target_collection.owner = new_owner;620            <Collection<T>>::insert(collection_id, target_collection);621622            Ok(())623        }624625        /// Adds an admin of the Collection.626        /// 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. 627        /// 628        /// # Permissions629        /// 630        /// * Collection Owner.631        /// * Collection Admin.632        /// 633        /// # Arguments634        /// 635        /// * collection_id: ID of the Collection to add admin for.636        /// 637        /// * new_admin_id: Address of new admin to add.638        #[weight = T::WeightInfo::add_collection_admin()]639        pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {640641            let sender = ensure_signed(origin)?;642            Self::check_owner_or_admin_permissions(collection_id, sender)?;643            let mut admin_arr: Vec<T::AccountId> = Vec::new();644645            if <AdminList<T>>::contains_key(collection_id)646            {647                admin_arr = <AdminList<T>>::get(collection_id);648                ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");649            }650651            // Number of collection admins652            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");653654            admin_arr.push(new_admin_id);655            <AdminList<T>>::insert(collection_id, admin_arr);656657            Ok(())658        }659660        /// 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.661        ///662        /// # Permissions663        /// 664        /// * Collection Owner.665        /// * Collection Admin.666        /// 667        /// # Arguments668        /// 669        /// * collection_id: ID of the Collection to remove admin for.670        /// 671        /// * account_id: Address of admin to remove.672        #[weight = T::WeightInfo::remove_collection_admin()]673        pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {674675            let sender = ensure_signed(origin)?;676            Self::check_owner_or_admin_permissions(collection_id, sender)?;677678            if <AdminList<T>>::contains_key(collection_id)679            {680                let mut admin_arr = <AdminList<T>>::get(collection_id);681                admin_arr.retain(|i| *i != account_id);682                <AdminList<T>>::insert(collection_id, admin_arr);683            }684685            Ok(())686        }687688        /// # Permissions689        /// 690        /// * Collection Owner691        /// 692        /// # Arguments693        /// 694        /// * collection_id.695        /// 696        /// * new_sponsor.697        #[weight = T::WeightInfo::set_collection_sponsor()]698        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {699700            let sender = ensure_signed(origin)?;701            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");702703            let mut target_collection = <Collection<T>>::get(collection_id);704            ensure!(sender == target_collection.owner, "You do not own this collection");705706            target_collection.unconfirmed_sponsor = new_sponsor;707            <Collection<T>>::insert(collection_id, target_collection);708709            Ok(())710        }711712        /// # Permissions713        /// 714        /// * Sponsor.715        /// 716        /// # Arguments717        /// 718        /// * collection_id.719        #[weight = T::WeightInfo::confirm_sponsorship()]720        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {721722            let sender = ensure_signed(origin)?;723            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");724725            let mut target_collection = <Collection<T>>::get(collection_id);726            ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");727728            target_collection.sponsor = target_collection.unconfirmed_sponsor;729            target_collection.unconfirmed_sponsor = T::AccountId::default();730            <Collection<T>>::insert(collection_id, target_collection);731732            Ok(())733        }734735        /// Switch back to pay-per-own-transaction model.736        ///737        /// # Permissions738        ///739        /// * Collection owner.740        /// 741        /// # Arguments742        /// 743        /// * collection_id.744        #[weight = T::WeightInfo::remove_collection_sponsor()]745        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {746747            let sender = ensure_signed(origin)?;748            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");749750            let mut target_collection = <Collection<T>>::get(collection_id);751            ensure!(sender == target_collection.owner, "You do not own this collection");752753            target_collection.sponsor = T::AccountId::default();754            <Collection<T>>::insert(collection_id, target_collection);755756            Ok(())757        }758759        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.760        /// 761        /// # Permissions762        /// 763        /// * Collection Owner.764        /// * Collection Admin.765        /// * Anyone if766        ///     * White List is enabled, and767        ///     * Address is added to white list, and768        ///     * MintPermission is enabled (see SetMintPermission method)769        /// 770        /// # Arguments771        /// 772        /// * collection_id: ID of the collection.773        /// 774        /// * properties: Array of bytes that contains NFT properties. Since NFT Module is agnostic of properties meaning, it is treated purely as an array of bytes.775        /// 776        /// * owner: Address, initial owner of the NFT.777        // #[weight =778        // (130_000_000 as Weight)779        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))780        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))781        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]782783        #[weight = T::WeightInfo::create_item(properties.len())]784        pub fn create_item(origin, collection_id: u64, properties: Vec<u8>, owner: T::AccountId) -> DispatchResult {785786            let sender = ensure_signed(origin)?;787            Self::collection_exists(collection_id)?;788            let target_collection = <Collection<T>>::get(collection_id);789790            if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {791                ensure!(target_collection.mint_mode == true, "Public minting is not allowed for this collection");792                Self::check_white_list(collection_id, &owner)?;793                Self::check_white_list(collection_id, &sender)?;794            }795796            match target_collection.mode797            {798                CollectionMode::NFT(_) => {799800                    // check size801                    ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");802803                    // Create nft item804                    let item = NftItemType {805                        collection: collection_id,806                        owner: owner,807                        data: properties.clone(),808                    };809810                    Self::add_nft_item(item)?;811812                },813                CollectionMode::Fungible(_) => {814815                    // check size816                    ensure!(properties.len() as u32 == 0, "Size of item must be 0 with fungible type");817818                    let item = FungibleItemType {819                        collection: collection_id,820                        owner: owner,821                        value: (10 as u128).pow(target_collection.decimal_points)822                    };823824                    Self::add_fungible_item(item)?;825                },826                CollectionMode::ReFungible(_, _) => {827828                    // check size829                    ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");830831                    let mut owner_list = Vec::new();832                    let value = (10 as u128).pow(target_collection.decimal_points);833                    owner_list.push(Ownership {owner: owner.clone(), fraction: value});834835                    let item = ReFungibleItemType {836                        collection: collection_id,837                        owner: owner_list,838                        data: properties.clone()839                    };840841                    Self::add_refungible_item(item)?;842                },843                _ => { ensure!(1 == 0,"just error"); }844845            };846847            // call event848            Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));849850            Ok(())851        }852853        /// Destroys a concrete instance of NFT.854        /// 855        /// # Permissions856        /// 857        /// * Collection Owner.858        /// * Collection Admin.859        /// * Current NFT Owner.860        /// 861        /// # Arguments862        /// 863        /// * collection_id: ID of the collection.864        /// 865        /// * item_id: ID of NFT to burn.866        #[weight = T::WeightInfo::burn_item()]867        pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {868869            let sender = ensure_signed(origin)?;870            Self::collection_exists(collection_id)?;871872            // Transfer permissions check873            let target_collection = <Collection<T>>::get(collection_id);874            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||875                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),876                "Only item owner, collection owner and admins can modify item");877878            if target_collection.access == AccessMode::WhiteList {879                Self::check_white_list(collection_id, &sender)?;880            }881882            match target_collection.mode883            {884                CollectionMode::NFT(_) => Self::burn_nft_item(collection_id, item_id)?,885                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,886                CollectionMode::ReFungible(_, _)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,887                _ => ()888            };889890            // call event891            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));892893            Ok(())894        }895896        /// Change ownership of the token.897        /// 898        /// # Permissions899        /// 900        /// * Collection Owner901        /// * Collection Admin902        /// * Current NFT owner903        ///904        /// # Arguments905        /// 906        /// * recipient: Address of token recipient.907        /// 908        /// * collection_id.909        /// 910        /// * item_id: ID of the item911        ///     * Non-Fungible Mode: Required.912        ///     * Fungible Mode: Ignored.913        ///     * Re-Fungible Mode: Required.914        /// 915        /// * value: Amount to transfer.916        ///     * Non-Fungible Mode: Ignored917        ///     * Fungible Mode: Must specify transferred amount918        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)919        #[weight = T::WeightInfo::transfer()]920        pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {921922            let sender = ensure_signed(origin)?;923924            // Transfer permissions check925            let target_collection = <Collection<T>>::get(collection_id);926            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||927                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),928                "Only item owner, collection owner and admins can modify item");929930            if target_collection.access == AccessMode::WhiteList {931                Self::check_white_list(collection_id, &sender)?;932                Self::check_white_list(collection_id, &recipient)?;933            }934935            match target_collection.mode936            {937                CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,938                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,939                CollectionMode::ReFungible(_, _)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,940                _ => ()941            };942943            Ok(())944        }945946        /// Set, change, or remove approved address to transfer the ownership of the NFT.947        /// 948        /// # Permissions949        /// 950        /// * Collection Owner951        /// * Collection Admin952        /// * Current NFT owner953        /// 954        /// # Arguments955        /// 956        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).957        /// 958        /// * collection_id.959        /// 960        /// * item_id: ID of the item.961        #[weight = T::WeightInfo::approve()]962        pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {963964            let sender = ensure_signed(origin)?;965966            // Transfer permissions check967            let target_collection = <Collection<T>>::get(collection_id);968            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||969                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),970                "Only item owner, collection owner and admins can approve");971972            if target_collection.access == AccessMode::WhiteList {973                Self::check_white_list(collection_id, &sender)?;974                Self::check_white_list(collection_id, &approved)?;975            }976977            // amount param stub978            let amount = 100000000;979980            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));981            if list_exists {982983                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));984                let item_contains = list.iter().any(|i| i.approved == approved);985986                if !item_contains {987                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });988                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);989                }990            } else {991992                let mut list = Vec::new();993                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });994                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);995            }996997            Ok(())998        }999        1000        /// 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.1001        /// 1002        /// # Permissions1003        /// * Collection Owner1004        /// * Collection Admin1005        /// * Current NFT owner1006        /// * Address approved by current NFT owner1007        /// 1008        /// # Arguments1009        /// 1010        /// * from: Address that owns token.1011        /// 1012        /// * recipient: Address of token recipient.1013        /// 1014        /// * collection_id.1015        /// 1016        /// * item_id: ID of the item.1017        /// 1018        /// * value: Amount to transfer.1019        #[weight = T::WeightInfo::transfer_from()]1020        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10211022            let sender = ensure_signed(origin)?;1023            let mut appoved_transfer = false;10241025            // Check approve1026            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1027                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1028                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1029                if opt_item.is_some()1030                {1031                    appoved_transfer = true;1032                    ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1033                }1034            }10351036            // Transfer permissions check1037            let target_collection = <Collection<T>>::get(collection_id);1038            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1039                "Only item owner, collection owner and admins can modify items");10401041            if target_collection.access == AccessMode::WhiteList {1042                Self::check_white_list(collection_id, &sender)?;1043                Self::check_white_list(collection_id, &recipient)?;1044            }10451046            // remove approve1047            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1048                .into_iter().filter(|i| i.approved != sender.clone()).collect();1049            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);105010511052            match target_collection.mode1053            {1054                CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, from, recipient)?,1055                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1056                CollectionMode::ReFungible(_, _)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1057                _ => ()1058            };10591060            Ok(())1061        }10621063        ///1064        #[weight = 0]1065        pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {10661067            // let no_perm_mes = "You do not have permissions to modify this collection";1068            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1069            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1070            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);10711072            // // on_nft_received  call10731074            // Self::transfer(origin, collection_id, item_id, new_owner)?;10751076            Ok(())1077        }10781079        /// Set off-chain data schema.1080        /// 1081        /// # Permissions1082        /// 1083        /// * Collection Owner1084        /// * Collection Admin1085        /// 1086        /// # Arguments1087        /// 1088        /// * collection_id.1089        /// 1090        /// * schema: String representing the offchain data schema.1091        #[weight = T::WeightInfo::set_offchain_schema()]1092        pub fn set_offchain_schema(1093            origin,1094            collection_id: u64,1095            schema: Vec<u8>1096        ) -> DispatchResult {1097            let sender = ensure_signed(origin)?;1098            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;10991100            let mut target_collection = <Collection<T>>::get(collection_id);1101            target_collection.offchain_schema = schema;1102            <Collection<T>>::insert(collection_id, target_collection);11031104            Ok(())1105        }11061107        // Sudo permissions function1108        #[weight = 0]1109        pub fn set_chain_limits(1110            origin,1111            limits: ChainLimits1112        ) -> DispatchResult {1113            ensure_root(origin)?;1114            <ChainLimit>::put(limits);1115            Ok(())1116        }11171118        /// Enable smart contract self-sponsoring.1119        /// 1120        /// # Permissions1121        /// 1122        /// * Contract Owner1123        /// 1124        /// # Arguments1125        /// 1126        /// * contract address1127        /// * enable flag1128        /// 1129        #[weight = 0]1130        pub fn enable_contract_sponsoring(1131            origin,1132            contract_address: T::AccountId,1133            enable: bool1134        ) -> DispatchResult {1135            let sender = ensure_signed(origin)?;1136            let mut is_owner = false;1137            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1138                let owner = <ContractOwner<T>>::get(&contract_address);1139                is_owner = sender == owner;1140            }1141            ensure!(is_owner, "Only contract owner may call this method");11421143            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1144            Ok(())1145        }11461147    }1148}11491150impl<T: Trait> Module<T> {1151    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1152        let current_index = <ItemListIndex>::get(item.collection)1153            .checked_add(1)1154            .expect("Item list index id error");1155        let itemcopy = item.clone();1156        let owner = item.owner.clone();1157        let value = item.value as u64;11581159        Self::add_token_index(item.collection, current_index, owner.clone())?;11601161        <ItemListIndex>::insert(item.collection, current_index);1162        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);11631164        // Add current block1165        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1166        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1167        1168        // Update balance1169        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1170            .checked_add(value)1171            .unwrap();1172        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);11731174        Ok(())1175    }11761177    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1178        let current_index = <ItemListIndex>::get(item.collection)1179            .checked_add(1)1180            .expect("Item list index id error");1181        let itemcopy = item.clone();11821183        let value = item.owner.first().unwrap().fraction as u64;1184        let owner = item.owner.first().unwrap().owner.clone();11851186        Self::add_token_index(item.collection, current_index, owner.clone())?;11871188        <ItemListIndex>::insert(item.collection, current_index);1189        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);11901191        // Add current block1192        let block_number: T::BlockNumber = 0.into();1193        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);11941195        // Update balance1196        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1197            .checked_add(value)1198            .unwrap();1199        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);12001201        Ok(())1202    }12031204    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1205        let current_index = <ItemListIndex>::get(item.collection)1206            .checked_add(1)1207            .expect("Item list index id error");12081209        let item_owner = item.owner.clone();1210        let collection_id = item.collection.clone();1211        Self::add_token_index(collection_id, current_index, item.owner.clone())?;12121213        <ItemListIndex>::insert(collection_id, current_index);1214        <NftItemList<T>>::insert(collection_id, current_index, item);12151216        // Add current block1217        let block_number: T::BlockNumber = 0.into();1218        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);12191220        // Update balance1221        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1222            .checked_add(1)1223            .unwrap();1224        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);12251226        Ok(())1227    }12281229    fn burn_refungible_item(1230        collection_id: u64,1231        item_id: u64,1232        owner: T::AccountId,1233    ) -> DispatchResult {1234        ensure!(1235            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1236            "Item does not exists"1237        );1238        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1239        let item = collection1240            .owner1241            .iter()1242            .filter(|&i| i.owner == owner)1243            .next()1244            .unwrap();1245        Self::remove_token_index(collection_id, item_id, owner.clone())?;12461247        // remove approve list1248        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));12491250        // update balance1251        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1252            .checked_sub(item.fraction as u64)1253            .unwrap();1254        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);12551256        <ReFungibleItemList<T>>::remove(collection_id, item_id);12571258        Ok(())1259    }12601261    fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1262        ensure!(1263            <NftItemList<T>>::contains_key(collection_id, item_id),1264            "Item does not exists"1265        );1266        let item = <NftItemList<T>>::get(collection_id, item_id);1267        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;12681269        // remove approve list1270        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));12711272        // update balance1273        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1274            .checked_sub(1)1275            .unwrap();1276        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1277        <NftItemList<T>>::remove(collection_id, item_id);12781279        Ok(())1280    }12811282    fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1283        ensure!(1284            <FungibleItemList<T>>::contains_key(collection_id, item_id),1285            "Item does not exists"1286        );1287        let item = <FungibleItemList<T>>::get(collection_id, item_id);1288        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;12891290        // remove approve list1291        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));12921293        // update balance1294        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1295            .checked_sub(item.value as u64)1296            .unwrap();1297        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);12981299        <FungibleItemList<T>>::remove(collection_id, item_id);13001301        Ok(())1302    }13031304    fn collection_exists(collection_id: u64) -> DispatchResult {1305        ensure!(1306            <Collection<T>>::contains_key(collection_id),1307            "This collection does not exist"1308        );1309        Ok(())1310    }13111312    fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1313        Self::collection_exists(collection_id)?;13141315        let target_collection = <Collection<T>>::get(collection_id);1316        ensure!(1317            subject == target_collection.owner,1318            "You do not own this collection"1319        );13201321        Ok(())1322    }13231324    fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1325        let target_collection = <Collection<T>>::get(collection_id);1326        let mut result: bool = subject == target_collection.owner;1327        let exists = <AdminList<T>>::contains_key(collection_id);13281329        if !result & exists {1330            if <AdminList<T>>::get(collection_id).contains(&subject) {1331                result = true1332            }1333        }13341335        result1336    }13371338    fn check_owner_or_admin_permissions(1339        collection_id: u64,1340        subject: T::AccountId,1341    ) -> DispatchResult {1342        Self::collection_exists(collection_id)?;1343        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());13441345        ensure!(1346            result,1347            "You do not have permissions to modify this collection"1348        );1349        Ok(())1350    }13511352    fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1353        let target_collection = <Collection<T>>::get(collection_id);13541355        match target_collection.mode {1356            CollectionMode::NFT(_) => {1357                <NftItemList<T>>::get(collection_id, item_id).owner == subject1358            }1359            CollectionMode::Fungible(_) => {1360                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1361            }1362            CollectionMode::ReFungible(_, _) => {1363                <ReFungibleItemList<T>>::get(collection_id, item_id)1364                    .owner1365                    .iter()1366                    .any(|i| i.owner == subject)1367            }1368            CollectionMode::Invalid => false,1369        }1370    }13711372    fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1373        let mes = "Address is not in white list";1374        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1375        let wl = <WhiteList<T>>::get(collection_id);1376        ensure!(wl.contains(address), mes);13771378        Ok(())1379    }13801381    fn transfer_fungible(1382        collection_id: u64,1383        item_id: u64,1384        value: u64,1385        owner: T::AccountId,1386        new_owner: T::AccountId,1387    ) -> DispatchResult {1388        ensure!(1389            <FungibleItemList<T>>::contains_key(collection_id, item_id),1390            "Item not exists"1391        );13921393        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1394        let amount = full_item.value;13951396        ensure!(amount >= value.into(), "Item balance not enouth");13971398        // update balance1399        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1400            .checked_sub(value)1401            .unwrap();1402        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);14031404        let mut new_owner_account_id = 0;1405        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1406        if new_owner_items.len() > 0 {1407            new_owner_account_id = new_owner_items[0];1408        }14091410        let val64 = value.into();14111412        // transfer1413        if amount == val64 && new_owner_account_id == 0 {1414            // change owner1415            // new owner do not have account1416            let mut new_full_item = full_item.clone();1417            new_full_item.owner = new_owner.clone();1418            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);14191420            // update balance1421            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1422                .checked_add(value)1423                .unwrap();1424            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14251426            // update index collection1427            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1428        } else {1429            let mut new_full_item = full_item.clone();1430            new_full_item.value -= val64;14311432            // separate amount1433            if new_owner_account_id > 0 {1434                // new owner has account1435                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1436                item.value += val64;14371438                // update balance1439                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1440                    .checked_add(value)1441                    .unwrap();1442                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14431444                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1445            } else {1446                // new owner do not have account1447                let item = FungibleItemType {1448                    collection: collection_id,1449                    owner: new_owner.clone(),1450                    value: val64,1451                };14521453                Self::add_fungible_item(item)?;1454            }14551456            if amount == val64 {1457                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;14581459                // remove approve list1460                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1461                <FungibleItemList<T>>::remove(collection_id, item_id);1462            }14631464            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1465        }14661467        Ok(())1468    }14691470    fn transfer_refungible(1471        collection_id: u64,1472        item_id: u64,1473        value: u64,1474        owner: T::AccountId,1475        new_owner: T::AccountId,1476    ) -> DispatchResult {1477        ensure!(1478            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1479            "Item not exists"1480        );14811482        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1483        let item = full_item1484            .owner1485            .iter()1486            .filter(|i| i.owner == owner)1487            .next()1488            .unwrap();1489        let amount = item.fraction;14901491        ensure!(amount >= value.into(), "Item balance not enouth");14921493        // update balance1494        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1495            .checked_sub(value)1496            .unwrap();1497        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);14981499        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1500            .checked_add(value)1501            .unwrap();1502        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15031504        let old_owner = item.owner.clone();1505        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1506        let val64 = value.into();15071508        // transfer1509        if amount == val64 && !new_owner_has_account {1510            // change owner1511            // new owner do not have account1512            let mut new_full_item = full_item.clone();1513            new_full_item1514                .owner1515                .iter_mut()1516                .find(|i| i.owner == owner)1517                .unwrap()1518                .owner = new_owner.clone();1519            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);15201521            // update index collection1522            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1523        } else {1524            let mut new_full_item = full_item.clone();1525            new_full_item1526                .owner1527                .iter_mut()1528                .find(|i| i.owner == owner)1529                .unwrap()1530                .fraction -= val64;15311532            // separate amount1533            if new_owner_has_account {1534                // new owner has account1535                new_full_item1536                    .owner1537                    .iter_mut()1538                    .find(|i| i.owner == new_owner)1539                    .unwrap()1540                    .fraction += val64;1541            } else {1542                // new owner do not have account1543                new_full_item.owner.push(Ownership {1544                    owner: new_owner.clone(),1545                    fraction: val64,1546                });1547                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1548            }15491550            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1551        }15521553        Ok(())1554    }15551556    fn transfer_nft(1557        collection_id: u64,1558        item_id: u64,1559        sender: T::AccountId,1560        new_owner: T::AccountId,1561    ) -> DispatchResult {1562        ensure!(1563            <NftItemList<T>>::contains_key(collection_id, item_id),1564            "Item not exists"1565        );15661567        let mut item = <NftItemList<T>>::get(collection_id, item_id);15681569        ensure!(1570            sender == item.owner,1571            "sender parameter and item owner must be equal"1572        );15731574        // update balance1575        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1576            .checked_sub(1)1577            .unwrap();1578        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);15791580        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1581            .checked_add(1)1582            .unwrap();1583        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15841585        // change owner1586        let old_owner = item.owner.clone();1587        item.owner = new_owner.clone();1588        <NftItemList<T>>::insert(collection_id, item_id, item);15891590        // update index collection1591        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;15921593        // reset approved list1594        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1595        Ok(())1596    }15971598    fn init_collection(item: &CollectionType<T::AccountId>) {1599        // check params1600        assert!(1601            item.decimal_points <= 4,1602            "decimal_points parameter must be lower than 4"1603        );1604        assert!(1605            item.name.len() <= 64,1606            "Collection name can not be longer than 63 char"1607        );1608        assert!(1609            item.name.len() <= 256,1610            "Collection description can not be longer than 255 char"1611        );1612        assert!(1613            item.token_prefix.len() <= 16,1614            "Token prefix can not be longer than 15 char"1615        );16161617        // Generate next collection ID1618        let next_id = CreatedCollectionCount::get()1619            .checked_add(1)1620            .expect("collection id error");16211622        CreatedCollectionCount::put(next_id);1623    }16241625    fn init_nft_token(item: &NftItemType<T::AccountId>) {1626        let current_index = <ItemListIndex>::get(item.collection)1627            .checked_add(1)1628            .expect("Item list index id error");16291630        let item_owner = item.owner.clone();1631        let collection_id = item.collection.clone();1632        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();16331634        <ItemListIndex>::insert(collection_id, current_index);16351636        // Update balance1637        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1638            .checked_add(1)1639            .unwrap();1640        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1641    }16421643    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1644        let current_index = <ItemListIndex>::get(item.collection)1645            .checked_add(1)1646            .expect("Item list index id error");1647        let owner = item.owner.clone();1648        let value = item.value as u64;16491650        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();16511652        <ItemListIndex>::insert(item.collection, current_index);16531654        // Update balance1655        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1656            .checked_add(value)1657            .unwrap();1658        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1659    }16601661    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1662        let current_index = <ItemListIndex>::get(item.collection)1663            .checked_add(1)1664            .expect("Item list index id error");16651666        let value = item.owner.first().unwrap().fraction as u64;1667        let owner = item.owner.first().unwrap().owner.clone();16681669        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();16701671        <ItemListIndex>::insert(item.collection, current_index);16721673        // Update balance1674        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1675            .checked_add(value)1676            .unwrap();1677        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1678    }16791680    fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {16811682        // add to account limit1683        if <AccountItemCount<T>>::contains_key(owner.clone()) {16841685            // bound Owned tokens by a single address1686            let count = <AccountItemCount<T>>::get(owner.clone());1687            ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");16881689            <AccountItemCount<T>>::insert(owner.clone(), 1690                count.checked_add(1).unwrap());1691        }1692        else {1693            <AccountItemCount<T>>::insert(owner.clone(), 1);1694        }16951696        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1697        if list_exists {1698            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1699            let item_contains = list.contains(&item_index.clone());17001701            if !item_contains {1702                list.push(item_index.clone());1703            }17041705            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1706        } else {1707            let mut itm = Vec::new();1708            itm.push(item_index.clone());1709            <AddressTokens<T>>::insert(collection_id, owner, itm);1710            1711        }17121713        Ok(())1714    }17151716    fn remove_token_index(1717        collection_id: u64,1718        item_index: u64,1719        owner: T::AccountId,1720    ) -> DispatchResult {17211722        // update counter1723        <AccountItemCount<T>>::insert(owner.clone(), 1724            <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());172517261727        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1728        if list_exists {1729            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1730            let item_contains = list.contains(&item_index.clone());17311732            if item_contains {1733                list.retain(|&item| item != item_index);1734                <AddressTokens<T>>::insert(collection_id, owner, list);1735            }1736        }17371738        Ok(())1739    }17401741    fn move_token_index(1742        collection_id: u64,1743        item_index: u64,1744        old_owner: T::AccountId,1745        new_owner: T::AccountId,1746    ) -> DispatchResult {1747        Self::remove_token_index(collection_id, item_index, old_owner)?;1748        Self::add_token_index(collection_id, item_index, new_owner)?;17491750        Ok(())1751    }1752}17531754////////////////////////////////////////////////////////////////////////////////////////////////////1755// Economic models1756// #region17571758/// Fee multiplier.1759pub type Multiplier = FixedU128;17601761type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1762    <T as system::Trait>::AccountId,1763>>::Balance;1764type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1765    <T as system::Trait>::AccountId,1766>>::NegativeImbalance;17671768/// Require the transactor pay for themselves and maybe include a tip to gain additional priority1769/// in the queue.1770#[derive(Encode, Decode, Clone, Eq, PartialEq)]1771pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(1772    #[codec(compact)] BalanceOf<T>1773);17741775impl<T: Trait + Send + Sync> sp_std::fmt::Debug1776    for ChargeTransactionPayment<T>1777{1778    #[cfg(feature = "std")]1779    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1780        write!(f, "ChargeTransactionPayment<{:?}>", self.0)1781    }1782    #[cfg(not(feature = "std"))]1783    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1784        Ok(())1785    }1786}17871788impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>1789where1790    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,1791    BalanceOf<T>: Send + Sync + FixedPointOperand,1792{1793    /// utility constructor. Used only in client/factory code.1794    pub fn from(fee: BalanceOf<T>) -> Self {1795        Self(fee)1796    }17971798    pub fn traditional_fee(1799        len: usize,1800        info: &DispatchInfoOf<T::Call>,1801        tip: BalanceOf<T>,1802    ) -> BalanceOf<T>1803    where1804        T::Call: Dispatchable<Info = DispatchInfo>,1805    {1806        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)1807    }18081809    fn withdraw_fee(1810        &self,1811        who: &T::AccountId,1812        call: &T::Call,1813        info: &DispatchInfoOf<T::Call>,1814        len: usize,1815    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {1816        let tip = self.0;18171818        // Set fee based on call type. Creating collection costs 1 Unique.1819        // All other transactions have traditional fees so far1820        // let fee = match call.is_sub_type() {1821        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),1822        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes1823        //                                                 // _ => <BalanceOf<T>>::from(100)1824        // };1825        let fee = Self::traditional_fee(len, info, tip);18261827        // Determine who is paying transaction fee based on ecnomic model1828        // Parse call to extract collection ID and access collection sponsor1829        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {1830            Some(Call::create_item(collection_id, _properties, _owner)) => {1831                <Collection<T>>::get(collection_id).sponsor1832            }1833            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {1834                let _collection_mode = <Collection<T>>::get(collection_id).mode;18351836                // sponsor timeout1837                let sponsor_transfer = match _collection_mode {1838                    CollectionMode::NFT(_) => {1839                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);1840                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1841                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1842                        if block_number >= limit_time {1843                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);1844                            true1845                        }1846                        else {1847                            false1848                        }1849                    }1850                    CollectionMode::Fungible(_) => {1851                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);1852                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1853                        if basket.iter().any(|i| i.address == _new_owner.clone())1854                        {1855                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();1856                            let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();1857                            if block_number >= limit_time {1858                                basket.retain(|x| x.address == item.address);1859                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });1860                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);1861                                true1862                            }1863                            else {1864                                false1865                            }1866                        }1867                        else {1868                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});1869                            true1870                        }1871                    }1872                    CollectionMode::ReFungible(_, _) => {1873                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);1874                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1875                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1876                        if block_number >= limit_time {1877                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);1878                            true1879                        } else {1880                            false1881                        }1882                    }1883                    _ => {1884                        false1885                    },1886                };18871888                if !sponsor_transfer {1889                    T::AccountId::default()1890                } else {1891                    <Collection<T>>::get(collection_id).sponsor1892                }1893            }18941895            _ => T::AccountId::default(),1896        };18971898        // Sponsor smart contracts1899        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {19001901            // On instantiation: set the contract owner1902            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {19031904                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(1905                    code_hash,1906                    &data,1907                    &who,1908                );1909                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());19101911                T::AccountId::default()1912            },19131914            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is1915            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {19161917                let mut sp = T::AccountId::default();1918                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());1919                if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {1920                    if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {1921                        sp = called_contract;1922                    }1923                }19241925                sp1926            },19271928            _ => sponsor,1929        };19301931        let mut who_pays_fee: T::AccountId = sponsor.clone();1932        if sponsor == T::AccountId::default() {1933            who_pays_fee = who.clone();1934        }19351936        // Only mess with balances if fee is not zero.1937        if fee.is_zero() {1938            return Ok((fee, None));1939        }19401941        match <T as transaction_payment::Trait>::Currency::withdraw(1942            &who_pays_fee,1943            fee,1944            if tip.is_zero() {1945                WithdrawReason::TransactionPayment.into()1946            } else {1947                WithdrawReason::TransactionPayment | WithdrawReason::Tip1948            },1949            ExistenceRequirement::KeepAlive,1950        ) {1951            Ok(imbalance) => Ok((fee, Some(imbalance))),1952            Err(_) => Err(InvalidTransaction::Payment.into()),1953        }1954    }1955}195619571958impl<T: Trait + Send + Sync> SignedExtension1959    for ChargeTransactionPayment<T>1960where1961    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,1962    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,1963{1964    const IDENTIFIER: &'static str = "ChargeTransactionPayment";1965    type AccountId = T::AccountId;1966    type Call = T::Call;1967    type AdditionalSigned = ();1968    type Pre = (1969        BalanceOf<T>,1970        Self::AccountId,1971        Option<NegativeImbalanceOf<T>>,1972        BalanceOf<T>,1973    );1974    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {1975        Ok(())1976    }19771978    fn validate(1979        &self,1980        _who: &Self::AccountId,1981        _call: &Self::Call,1982        _info: &DispatchInfoOf<Self::Call>,1983        _len: usize,1984    ) -> TransactionValidity {1985        Ok(ValidTransaction::default())1986    }19871988    fn pre_dispatch(1989        self,1990        who: &Self::AccountId,1991        call: &Self::Call,1992        info: &DispatchInfoOf<Self::Call>,1993        len: usize,1994    ) -> Result<Self::Pre, TransactionValidityError> {1995        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;1996        Ok((self.0, who.clone(), imbalance, fee))1997    }19981999    fn post_dispatch(2000        pre: Self::Pre,2001        info: &DispatchInfoOf<Self::Call>,2002        post_info: &PostDispatchInfoOf<Self::Call>,2003        len: usize,2004        _result: &DispatchResult,2005    ) -> Result<(), TransactionValidityError> {2006        let (tip, who, imbalance, fee) = pre;2007        if let Some(payed) = imbalance {2008            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2009                len as u32, info, post_info, tip,2010            );2011            let refund = fee.saturating_sub(actual_fee);2012            let actual_payment =2013                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2014                    &who, refund,2015                ) {2016                    Ok(refund_imbalance) => {2017                        // The refund cannot be larger than the up front payed max weight.2018                        // `PostDispatchInfo::calc_unspent` guards against such a case.2019                        match payed.offset(refund_imbalance) {2020                            Ok(actual_payment) => actual_payment,2021                            Err(_) => return Err(InvalidTransaction::Payment.into()),2022                        }2023                    }2024                    // We do not recreate the account using the refund. The up front payment2025                    // is gone in that case.2026                    Err(_) => payed,2027                };2028            let imbalances = actual_payment.split(tip);2029            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2030                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2031            );2032        }2033        Ok(())2034    }2035}20362037// #endregion20382039